vaguely working upload/download implementation

This commit is contained in:
Joseph Montanaro 2022-01-16 22:31:20 -08:00
commit 00f4dce4f9
11 changed files with 1707 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
db/punt.sqlite
uploads

1542
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

27
Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[package]
name = "punt"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = {version = "0.5.0-rc.1", features = ["json"]}
serde = {version = "1", features = ["derive"]}
tokio = {version = "1", features = ["fs"]}
serde_json = "1"
rand = "0.8"
[dependencies.libsqlite3-sys]
version = ">=0.17.2, <0.24.0"
features = ["bundled"]
[dependencies.diesel]
version = "1.4"
default-features = false
features = ["sqlite"]
[dependencies.rocket_sync_db_pools]
version = "0.1.0-rc.1"
default-features = false
features = ["diesel_sqlite_pool"]

4
Rocket.toml Normal file
View File

@ -0,0 +1,4 @@
# Temporary, will be replaced by env vars and hard-coded defaults eventually so that Punt can function as a single-binary application
[global.databases]
punt = {url = "db/punt.sqlite"}

View File

@ -0,0 +1 @@
DROP TABLE uploads;

View File

@ -0,0 +1,5 @@
CREATE TABLE uploads (
id TEXT PRIMARY KEY NOT NULL,
name TEXT,
created_at INTEGER
);

9
diesel.toml Normal file
View File

@ -0,0 +1,9 @@
# For documentation on how to configure this file,
# see diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "src/schema.rs"
# this will start working whenever Diesel CLI gets around to releasing v1.4.4
# [migrations_directory]
# file = "db/migrations"

40
src/data.rs Normal file
View File

@ -0,0 +1,40 @@
use rocket_sync_db_pools::database;
use rocket::serde::Serialize;
use diesel::prelude::*;
use crate::schema::uploads;
#[database("punt")]
pub struct DbConn(diesel::SqliteConnection);
#[derive(Queryable, Insertable, Serialize)]
#[table_name="uploads"]
pub struct FileInfo {
pub id: String,
pub name: Option<String>,
pub created_at: Option<i32> // figure out how to marshal this to a proper time type later
}
pub async fn get_file_info(id: String, conn: DbConn) -> QueryResult<FileInfo> {
conn.run(|c| uploads::table.find(id).first(c)).await
}
pub async fn put_file_info(id: &str, name: Option<&str>, conn: DbConn) -> QueryResult<()> {
let file_info = FileInfo {
id: id.to_owned(),
name: name.map(|n| n.to_owned()),
created_at: Some(1642371865),
};
conn.run(|c| {
diesel::insert_into(uploads::table)
.values(file_info)
.execute(c)
}).await?;
Ok(())
}

25
src/lib.rs Normal file
View File

@ -0,0 +1,25 @@
use rand::{Rng, distributions::Alphanumeric};
use tokio::fs::create_dir;
use std::io::ErrorKind;
use rocket::fs::TempFile;
pub fn gen_id() -> String {
rand::thread_rng()
.sample_iter(Alphanumeric)
.map(|c| c as char)
.take(16)
.collect()
}
pub async fn persist_file(file: &mut TempFile<'_>, filename: &str) -> std::io::Result<()> {
let dst = format!("uploads/{filename}");
match file.persist_to(&dst).await {
Err(e) if e.kind() == ErrorKind::NotFound => {
create_dir("uploads").await?;
file.persist_to(&dst).await
},
other => other,
}
}

44
src/main.rs Normal file
View File

@ -0,0 +1,44 @@
use rocket::{get, post, routes, fs::TempFile};
use rocket::http::Status;
use rocket::serde::json::Json;
#[macro_use] extern crate diesel;
mod data;
mod lib;
mod schema;
use data::{DbConn, FileInfo};
#[get("/")]
fn test() -> &'static str {
return "Hello, world!";
}
#[get("/<id>")]
async fn download(id: String, conn: DbConn) -> Result<Json<FileInfo>, Status> {
data::get_file_info(id, conn).await
.map(|info| Json(info))
.map_err(|_| Status::InternalServerError)
}
#[post("/upload", data = "<file>")]
async fn upload(mut file: TempFile<'_>, conn: DbConn) -> Result<String, Status> {
let id = lib::gen_id();
data::put_file_info(&id, file.name(), conn).await
.map_err(|_| Status::InternalServerError)?;
lib::persist_file(&mut file, &id).await
.map_err(|_| Status::InternalServerError)?;
Ok(id)
}
#[rocket::launch]
fn rocket() -> _ {
rocket::build()
.attach(DbConn::fairing())
.mount("/", routes![test, upload, download])
}

7
src/schema.rs Normal file
View File

@ -0,0 +1,7 @@
table! {
uploads (id) {
id -> Text,
name -> Nullable<Text>,
created_at -> Nullable<Integer>,
}
}