Browse Source

wip state (building)

xenua 2 years ago
parent
commit
09b60656da
  1. 3
      .gitignore
  2. 5
      Cargo.lock
  3. 1
      Cargo.toml
  4. 8
      src/admin.rs
  5. 116
      src/data.rs
  6. 73
      src/down.rs
  7. 79
      src/main.rs
  8. 5
      src/up.rs
  9. 7
      src/user.rs
  10. 4
      templates/base.html
  11. 5
      templates/file.html
  12. 7
      templates/text.html

3
.gitignore vendored

@ -1,2 +1,5 @@ @@ -1,2 +1,5 @@
/target
.idea/
test_data/
.env

5
Cargo.lock generated

@ -663,9 +663,9 @@ dependencies = [ @@ -663,9 +663,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "parking_lot"
@ -1328,6 +1328,7 @@ dependencies = [ @@ -1328,6 +1328,7 @@ dependencies = [
"dashmap",
"lazy_static",
"nanoid",
"once_cell",
"parking_lot",
"serde",
"tera",

1
Cargo.toml

@ -12,6 +12,7 @@ axum = "0.6.1" @@ -12,6 +12,7 @@ axum = "0.6.1"
dashmap = "5.4.0"
lazy_static = "1.4.0"
nanoid = "0.4.0"
once_cell = "1.17.0"
parking_lot = "0.12.1"
serde = { version = "1.0.152", features = ["derive", "serde_derive"] }
tera = "1.17.1"

8
src/admin.rs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
use axum::{routing::{post, get}, Router};
pub(crate) fn routes() -> Router {
Router::new()
.route("/", get(todo!()))
.route("/user", get(todo!()).post(todo!()))
.route("/user/:id", get(todo!()).post(todo!()))
}

116
src/data.rs

@ -1,16 +1,18 @@ @@ -1,16 +1,18 @@
use std::{
collections::HashMap,
fs::{read_to_string, remove_file, DirBuilder, File},
io::{self, BufRead},
os::unix::fs::DirBuilderExt,
path::PathBuf,
};
use std::os::unix::fs::DirBuilderExt;
use argon2::{
password_hash::{rand_core::OsRng, SaltString},
Argon2, PasswordHash, PasswordHasher, PasswordVerifier,
};
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use dashmap::DashMap;
use lazy_static::lazy_static;
@ -43,6 +45,18 @@ pub enum Error { @@ -43,6 +45,18 @@ pub enum Error {
BadPassword,
}
impl IntoResponse for Error {
fn into_response(self) -> Response {
match self {
Error::ShitMissing(thing) => {
(StatusCode::NOT_FOUND, format!("404 not found: {}", thing)).into_response()
}
_ => (StatusCode::INTERNAL_SERVER_ERROR, "shit went wrong sorry").into_response(),
}
}
}
fn meta_str_to_map(from: &str) -> Result<HashMap<&str, String>, Error> {
let map = from
.lines()
@ -67,12 +81,14 @@ fn hash_password(password: String) -> Result<String, Error> { @@ -67,12 +81,14 @@ fn hash_password(password: String) -> Result<String, Error> {
Ok(hash)
}
struct TextPaste {
#[derive(Clone, Debug)]
pub struct TextPaste {
pub text: String,
pub meta: TextMeta,
}
struct TextMeta {
#[derive(Clone, Debug)]
pub struct TextMeta {
language: Option<String>,
}
@ -95,8 +111,10 @@ impl std::fmt::Display for TextMeta { @@ -95,8 +111,10 @@ impl std::fmt::Display for TextMeta {
}
}
struct FileMeta {
file_name: String,
#[derive(Clone, Debug)]
pub struct FileMeta {
pub file_name: String,
pub size: u64,
}
impl FileMeta {
@ -104,17 +122,22 @@ impl FileMeta { @@ -104,17 +122,22 @@ impl FileMeta {
let mut map = meta_str_to_map(from)?;
let file_name = map.remove("file_name").ok_or(Error::Metadata)?;
Ok(Self { file_name })
let size = map
.remove("size")
.and_then(|string| string.parse::<u64>().ok())
.ok_or(Error::Metadata)?;
Ok(Self { file_name, size })
}
}
impl std::fmt::Display for FileMeta {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "file_name: {}\n", self.file_name)
write!(f, "file_name: {}\nsize: {}\n", self.file_name, self.size)
}
}
struct UserData {
#[derive(Clone, Debug)]
pub struct UserData {
pub username: String,
pub pw_hash: String,
pub is_admin: bool,
@ -146,7 +169,7 @@ impl UserData { @@ -146,7 +169,7 @@ impl UserData {
pub fn set_password_checked(&mut self, old: String, new: String) -> Result<(), Error> {
self.validate_password(old)?;
self.set_password(new);
self.set_password(new)?;
Ok(())
}
@ -187,15 +210,40 @@ impl DataStorage { @@ -187,15 +210,40 @@ impl DataStorage {
}
pub fn init(&mut self) -> Result<(), Error> {
println!("1");
self.ensure_folder_structure()?;
println!("2");
self.load_redirects()?;
println!("3");
self.load_text()?;
println!("4");
self.load_files()?;
println!("lets go");
self.load_users()?;
self.print_init_message();
Ok(())
}
fn print_init_message(&self) {
println!(
"data storage finished initializing. data loaded from {}:\n- {} redirects\n- {} users\n- {} text pastes\n- {} file/media pastes",
self.base_dir.display(),
self.redirects.len(),
self.users.len(),
self.text.len(),
self.files.len(),
);
}
pub fn get_text(&self, id: String) -> Result<TextPaste, Error> {
self.text
.get(&id)
.map(|thing| thing.value().clone())
.ok_or(Error::ShitMissing(id))
}
pub fn create_text(&mut self, id: String, text: String, meta: TextMeta) -> Result<(), Error> {
if self.text.contains_key(&id) {
return Err(Error::IDCollision);
@ -232,6 +280,14 @@ impl DataStorage { @@ -232,6 +280,14 @@ impl DataStorage {
Ok(old_text)
}
pub fn get_file_meta(&self, id: String) -> Result<FileMeta, Error> {
Ok(self
.files
.get(&id)
.map(|item| item.value().clone())
.ok_or(Error::ShitMissing(id))?)
}
pub fn create_file(&mut self, id: String, file_meta: FileMeta) -> Result<(), Error> {
if self.files.contains_key(&id) {
return Err(Error::IDCollision);
@ -239,16 +295,14 @@ impl DataStorage { @@ -239,16 +295,14 @@ impl DataStorage {
self.files.insert(id.clone(), file_meta);
self.write_file_meta(id);
self.write_file_meta(id)?;
Ok(())
}
pub fn delete_file(&mut self, id: String) -> Result<(), Error> {
let file_meta = self
.files
self.files
.remove(&id)
.ok_or(Error::ShitMissing(id.clone()))?
.1;
.ok_or(Error::ShitMissing(id.clone()))?;
remove_file(self.base_dir.join(FILE_DIR.as_str()).join(id.clone()))?;
remove_file(self.base_dir.join(FILE_DIR.as_str()).join(id + ".meta"))?;
@ -256,6 +310,13 @@ impl DataStorage { @@ -256,6 +310,13 @@ impl DataStorage {
Ok(())
}
pub fn get_redirect(&self, id: String) -> Result<String, Error> {
self.redirects
.get(&id)
.map(|meow| meow.value().to_string())
.ok_or(Error::ShitMissing(id))
}
pub fn create_redirect(&mut self, id: String, target: String) -> Result<(), Error> {
if self.redirects.contains_key(&id) {
return Err(Error::IDCollision);
@ -335,18 +396,23 @@ impl DataStorage { @@ -335,18 +396,23 @@ impl DataStorage {
dir_builder.create(self.base_dir.as_path())?;
dir_builder.create(self.base_dir.join(FILE_DIR.as_str()))?;
dir_builder.create(self.base_dir.join(TEXT_DIR.as_str()))?;
let redir_file = self.base_dir.join(REDIRECTS_FILE.as_str());
if !redir_file.exists() {
File::create(redir_file)?;
}
let users_file = self.base_dir.join(USERS_FILE.as_str());
if !users_file.exists() {
File::create(users_file)?;
}
Ok(())
}
fn load_redirects(&mut self) -> Result<(), Error> {
let f = File::options()
.create(true)
.read(true)
.open(self.base_dir.join(REDIRECTS_FILE.as_str()))?;
let contents = read_to_string(self.base_dir.join(REDIRECTS_FILE.as_str()))?;
for line in io::BufReader::new(f).lines() {
let (id, url) = line?
for line in contents.lines() {
let (id, url) = line
.split_once(": ")
.map(|(id, url)| (id.to_string(), url.to_string()))
.ok_or(Error::Metadata)?;
@ -411,7 +477,7 @@ impl DataStorage { @@ -411,7 +477,7 @@ impl DataStorage {
for line in read_to_string(self.base_dir.join(USERS_FILE.as_str()))?.lines() {
if line.starts_with("---") {
is_admin = false;
break;
continue;
}
let user = UserData::parse(line, is_admin)?;
self.users.insert(user.username.clone(), user);
@ -420,7 +486,7 @@ impl DataStorage { @@ -420,7 +486,7 @@ impl DataStorage {
}
fn write_redirects(&self) -> Result<(), Error> {
let mut fuckery: String = self
let fuckery: String = self
.redirects
.iter()
.map(|meow| format!("{}: {}\n", meow.key(), meow.value()))
@ -432,7 +498,7 @@ impl DataStorage { @@ -432,7 +498,7 @@ impl DataStorage {
fn write_users(&self) -> Result<(), Error> {
let mut fuckery = String::from("---");
self.users.iter().map(|item| {
self.users.iter().for_each(|item| {
if item.value().is_admin {
fuckery = format!("{}\n{}", item.value().to_string(), fuckery)
} else {

73
src/down.rs

@ -1,11 +1,64 @@ @@ -1,11 +1,64 @@
use axum::{routing::get, Router};
pub(crate) fn routes() -> Router {
Router::new()
.route("/t/:id", get(todo!()))
.route("/f/:id", get(todo!()))
.route("/l/:id", get(todo!()))
.route("/text/:id", get(todo!()))
.route("/file/:id", get(todo!()))
.route("/link/:id", get(todo!()))
use axum::{
extract::{Path, State},
http::{header, StatusCode},
response::{Html, IntoResponse},
routing::get,
Router,
};
use crate::{AppState, Error};
pub(crate) fn routes(router: Router<AppState>) -> Router<AppState> {
router
.route("/t/:id", get(get_text_page))
.route("/t/:id/raw", get(get_text_raw))
.route("/f/:id", get(get_file_page))
.route("/f/:id/dl", get(|| async { "Hello world" }))
.route("/l/:id", get(get_redirect))
.route("/text/:id", get(get_text_page))
.route("/text/:id/raw", get(get_text_raw))
.route("/file/:id", get(get_file_page))
.route("/file/:id/download", get(|| async { "Hello world" }))
.route("/link/:id", get(get_redirect))
}
async fn get_redirect(State(state): State<AppState>, Path(id): Path<String>) -> impl IntoResponse {
match state.cache.get_redirect(id) {
Ok(target) => (StatusCode::FOUND, [(header::LOCATION, target)]).into_response(),
Err(e) => e.into_response(),
}
}
async fn get_text_raw(State(state): State<AppState>, Path(id): Path<String>) -> impl IntoResponse {
match state.cache.get_text(id) {
Ok(text) => text.text.into_response(),
Err(e) => e.into_response(),
}
}
async fn get_text_page(
State(state): State<AppState>,
Path(id): Path<String>,
) -> Result<Html<String>, Error> {
let text = state.cache.get_text(id)?;
let mut ctx = tera::Context::new();
ctx.insert("text", &text.text);
return Ok(Html(state.tera.render("text.html", &ctx)?));
}
async fn get_file_page(
State(state): State<AppState>,
Path(id): Path<String>,
) -> Result<Html<String>, Error> {
let file_meta = state.cache.get_file_meta(id)?;
let mut ctx = tera::Context::new();
ctx.insert("size", &file_meta.size);
ctx.insert("file_name", &file_meta.file_name);
return Ok(Html(state.tera.render("file.html", &ctx)?));
}

79
src/main.rs

@ -1,71 +1,94 @@ @@ -1,71 +1,94 @@
#![allow(dead_code)]
mod data;
mod down;
mod up;
//mod up;
//mod user;
//mod admin;
use std::{net::SocketAddr, path::PathBuf};
use std::{net::SocketAddr, path::PathBuf, sync::Arc};
use axum::{routing::get, Router};
use lazy_static::lazy_static;
use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::get, Router};
use tera::Tera;
use data::DataStorage;
lazy_static! {
pub static ref TEMPLATES: Tera = {
let tera = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*.html"))
.expect("Parsing error(s)");
tera
};
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("DataStorage error")]
DataStorage(#[from] data::Error),
#[error("invalid env var")]
EnvVar,
#[error("invalid or missing env var: {0}")]
EnvVar(String),
#[error("tera error")]
Tera(#[from] tera::Error),
}
impl IntoResponse for Error {
fn into_response(self) -> axum::response::Response {
match self {
Error::DataStorage(ds_error) => ds_error.into_response(),
_ => (StatusCode::INTERNAL_SERVER_ERROR, "shit went wrong sorry").into_response(),
}
}
}
#[derive(Clone)]
struct AppState {
alphabet: Vec<char>,
id_length: usize,
cache: DataStorage,
cache: Arc<DataStorage>,
tera: Tera,
}
impl AppState {
pub fn new() -> Result<Self, Error> {
let base_dir_string = std::env::var("V_BASE_DIR").or(Err(Error::EnvVar))?;
let base_dir_string =
std::env::var("V_BASE_DIR").or(Err(Error::EnvVar("V_BASE_DIR".to_string())))?;
let id_length: usize = std::env::var("V_ID_LENGTH")
.unwrap_or("6".to_string())
.parse()
.or(Err(Error::EnvVar))?;
.or(Err(Error::EnvVar("V_ID_LENGTH".to_string())))?;
let alphabet = std::env::var("V_ID_ALPHABET")
.unwrap_or("abcdefghijkmnpqrstuwxyz123467890".to_string())
.chars()
.collect();
let base_dir = PathBuf::new();
let mut base_dir = PathBuf::new();
base_dir.push(base_dir_string);
let mut cache = DataStorage::new(base_dir);
cache.init()?;
let tera = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*.html"))
.expect("Parsing error(s)");
Ok(Self {
alphabet,
id_length,
cache,
cache: Arc::new(cache),
tera,
})
}
}
fn routes() -> Router {
let up = up::routes();
let down = down::routes();
Router::new()
.route("/", get(|| async { "Hello, World!" }))
.merge(down)
.nest("/up", up)
fn routes() -> Result<Router, Error> {
let state = AppState::new()?;
// let up = up::routes();
// let down = down::routes();
// let user = user::routes();
// let admin = admin::routes();
let router = Router::new().route("/extrac", get(test_extrac));
// .merge(down)
// .nest("/up", up)
// .nest("/user", user)
// .nest("/admin", admin)
let meow = down::routes(router);
Ok(meow.with_state(state))
}
async fn test_extrac(State(state): State<AppState>) -> String {
state.id_length.to_string()
}
#[tokio::main]
@ -75,7 +98,7 @@ async fn main() -> anyhow::Result<()> { @@ -75,7 +98,7 @@ async fn main() -> anyhow::Result<()> {
.parse()
.expect("LISTEN_ADDR parse error");
let app = routes();
let app = routes()?;
axum::Server::bind(&addr)
.serve(app.into_make_service())

5
src/up.rs

@ -1,8 +1,11 @@ @@ -1,8 +1,11 @@
use axum::{routing::post, Router};
use axum::{routing::{post, get}, Router};
pub(crate) fn routes() -> Router {
Router::new()
.route("/text", post(todo!()))
.route("/file", post(todo!()))
.route("/link", post(todo!()))
.route("/text/:id", get(todo!()).post(todo!()))
.route("/file/:id", get(todo!()).post(todo!()))
.route("/link/:id", get(todo!()).post(todo!()))
}

7
src/user.rs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
use axum::{Router, routing::post};
pub(crate) fn routes() -> Router {
Router::new()
.route("/password", post(todo!()))
.route("/login", post(todo!()))
}

4
templates/base.html

@ -10,13 +10,13 @@ @@ -10,13 +10,13 @@
<link rel="stylesheet" href="static/main.css" type="text/css" />
{% endblock head %}
</head>
<body {% block bodytags %}{% endblock bodytags %}>
<body>
{% block body %}
<header>
{% block header %}{% endblock header %}
</header>
<main>
{% block main %}{% enblock main %}
{% block main %}{% endblock main %}
</main>
<footer>
{% block footer %}{% endblock footer %}

5
templates/file.html

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block main %}
file {{ file_name }} ({{ size | filesizeformat }})
{% endblock main %}

7
templates/text.html

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block main %}
{{ text }}
{% endblock main %}
Loading…
Cancel
Save