mirror of
https://github.com/cupcakearmy/cryptgeon.git
synced 2025-09-05 17:00:39 +00:00
move folder
This commit is contained in:
2126
backend/Cargo.lock
generated
Normal file
2126
backend/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
backend/Cargo.toml
Normal file
24
backend/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "cryptgeon"
|
||||
version = "1.3.2"
|
||||
authors = ["cupcakearmy <hi@nicco.io>"]
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "cryptgeon"
|
||||
path = "src/main.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix-web = "3"
|
||||
actix-files = "0.5"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
lazy_static = "1"
|
||||
ring = "0.16"
|
||||
bs62 = "0.1"
|
||||
memcache = "0.16"
|
||||
byte-unit = "4"
|
||||
dotenv = "0.15"
|
||||
mime = "0.3"
|
10
backend/src/client.rs
Normal file
10
backend/src/client.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use actix_files::{Files, NamedFile};
|
||||
use actix_web::{web, Responder};
|
||||
|
||||
pub fn init(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(Files::new("/", "./frontend/build").index_file("index.html"));
|
||||
}
|
||||
|
||||
pub async fn fallback_fn() -> impl Responder {
|
||||
NamedFile::open("./frontend/build/index.html")
|
||||
}
|
27
backend/src/main.rs
Normal file
27
backend/src/main.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use actix_web::{middleware, web, App, HttpServer};
|
||||
use dotenv::dotenv;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
mod client;
|
||||
mod note;
|
||||
mod size;
|
||||
mod store;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
dotenv().ok();
|
||||
return HttpServer::new(|| {
|
||||
App::new()
|
||||
.wrap(middleware::Compress::default())
|
||||
.wrap(middleware::DefaultHeaders::default())
|
||||
.configure(size::init)
|
||||
.configure(note::init)
|
||||
.configure(client::init)
|
||||
.default_service(web::resource("").route(web::get().to(client::fallback_fn)))
|
||||
})
|
||||
.bind("0.0.0.0:5000")?
|
||||
.run()
|
||||
.await;
|
||||
}
|
5
backend/src/note/mod.rs
Normal file
5
backend/src/note/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod model;
|
||||
mod routes;
|
||||
|
||||
pub use model::*;
|
||||
pub use routes::*;
|
27
backend/src/note/model.rs
Normal file
27
backend/src/note/model.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use bs62;
|
||||
use ring::rand::SecureRandom;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Note {
|
||||
pub meta: String,
|
||||
pub contents: String,
|
||||
pub views: Option<u8>,
|
||||
pub expiration: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct NoteInfo {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct NotePublic {
|
||||
pub meta: String,
|
||||
pub contents: String,
|
||||
}
|
||||
|
||||
pub fn generate_id() -> String {
|
||||
let mut id: [u8; 32] = [0; 32];
|
||||
let sr = ring::rand::SystemRandom::new();
|
||||
let _ = sr.fill(&mut id);
|
||||
return bs62::encode_data(&id);
|
||||
}
|
137
backend/src/note/routes.rs
Normal file
137
backend/src/note/routes.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use actix_web::{delete, get, post, web, HttpResponse, Responder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::SystemTime;
|
||||
|
||||
use crate::note::{generate_id, Note, NoteInfo, NotePublic};
|
||||
use crate::size::LIMIT;
|
||||
use crate::store;
|
||||
|
||||
pub fn now() -> u32 {
|
||||
SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as u32
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct NotePath {
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[get("/{id}")]
|
||||
async fn one(path: web::Path<NotePath>) -> impl Responder {
|
||||
let p = path.into_inner();
|
||||
let note = store::get(&p.id);
|
||||
match note {
|
||||
None => return HttpResponse::NotFound().finish(),
|
||||
Some(_) => return HttpResponse::Ok().json(NoteInfo {}),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct CreateResponse {
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[post("/")]
|
||||
async fn create(note: web::Json<Note>) -> impl Responder {
|
||||
let mut n = note.into_inner();
|
||||
let id = generate_id();
|
||||
let bad_req = HttpResponse::BadRequest().finish();
|
||||
if n.views == None && n.expiration == None {
|
||||
return bad_req;
|
||||
}
|
||||
match n.views {
|
||||
Some(v) => {
|
||||
if v > 100 {
|
||||
return bad_req;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
match n.expiration {
|
||||
Some(e) => {
|
||||
if e > 360 {
|
||||
return bad_req;
|
||||
}
|
||||
let expiration = now() + (e * 60);
|
||||
n.expiration = Some(expiration);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
store::set(&id.clone(), &n.clone());
|
||||
return HttpResponse::Ok().json(CreateResponse { id: id });
|
||||
}
|
||||
|
||||
#[delete("/{id}")]
|
||||
async fn delete(path: web::Path<NotePath>) -> impl Responder {
|
||||
let p = path.into_inner();
|
||||
let note = store::get(&p.id);
|
||||
match note {
|
||||
None => return HttpResponse::NotFound().finish(),
|
||||
Some(note) => {
|
||||
let mut changed = note.clone();
|
||||
if changed.views == None && changed.expiration == None {
|
||||
return HttpResponse::BadRequest().finish();
|
||||
}
|
||||
match changed.views {
|
||||
Some(v) => {
|
||||
changed.views = Some(v - 1);
|
||||
let id = p.id.clone();
|
||||
if v <= 1 {
|
||||
store::del(&id);
|
||||
} else {
|
||||
store::set(&id, &changed.clone());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let n = now();
|
||||
match changed.expiration {
|
||||
Some(e) => {
|
||||
if e < n {
|
||||
store::del(&p.id.clone());
|
||||
return HttpResponse::BadRequest().finish();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return HttpResponse::Ok().json(NotePublic {
|
||||
contents: changed.contents,
|
||||
meta: changed.meta,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Status {
|
||||
version: String,
|
||||
max_size: usize,
|
||||
}
|
||||
|
||||
#[get("/status")]
|
||||
async fn status() -> impl Responder {
|
||||
println!("Limit: {}", *LIMIT);
|
||||
return HttpResponse::Ok().json(Status {
|
||||
version: option_env!("CARGO_PKG_VERSION")
|
||||
.unwrap_or("Unknown")
|
||||
.to_string(),
|
||||
max_size: *LIMIT,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn init(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("/api")
|
||||
.service(
|
||||
web::scope("/notes")
|
||||
.service(one)
|
||||
.service(create)
|
||||
.service(delete)
|
||||
.service(status),
|
||||
)
|
||||
.service(status),
|
||||
);
|
||||
}
|
20
backend/src/size.rs
Normal file
20
backend/src/size.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use actix_web::web;
|
||||
use byte_unit::Byte;
|
||||
use mime;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref LIMIT: usize =
|
||||
Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string()))
|
||||
.unwrap()
|
||||
.get_bytes() as usize;
|
||||
}
|
||||
|
||||
pub fn init(cfg: &mut web::ServiceConfig) {
|
||||
println!("Limit: {}", *LIMIT);
|
||||
let json = web::JsonConfig::default().limit(*LIMIT);
|
||||
let plain = web::PayloadConfig::default()
|
||||
.limit(*LIMIT)
|
||||
.mimetype(mime::STAR_STAR);
|
||||
cfg.data(json);
|
||||
cfg.data(plain);
|
||||
}
|
36
backend/src/store.rs
Normal file
36
backend/src/store.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use memcache;
|
||||
|
||||
use crate::note::now;
|
||||
use crate::note::Note;
|
||||
|
||||
lazy_static! {
|
||||
static ref CLIENT: memcache::Client = memcache::connect(format!(
|
||||
"memcache://{}?timeout=10&tcp_nodelay=true",
|
||||
std::env::var("MEMCACHE").unwrap_or("127.0.0.1:11211".to_string())
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn set(id: &String, note: &Note) {
|
||||
let serialized = serde_json::to_string(¬e.clone()).unwrap();
|
||||
let expiration: u32 = match note.expiration {
|
||||
Some(e) => e - now(),
|
||||
None => 0,
|
||||
};
|
||||
CLIENT.set(id, serialized, expiration).unwrap();
|
||||
}
|
||||
|
||||
pub fn get(id: &String) -> Option<Note> {
|
||||
let value: Option<String> = CLIENT.get(&id).unwrap();
|
||||
match value {
|
||||
None => return None,
|
||||
Some(s) => {
|
||||
let deserialize: Note = serde_json::from_str(&s).unwrap();
|
||||
return Some(deserialize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn del(id: &String) {
|
||||
CLIENT.delete(id).unwrap();
|
||||
}
|
Reference in New Issue
Block a user