finish image paths, you can now create, delete, etc. images
This commit is contained in:
parent
82a42ad0df
commit
0644f034a8
|
@ -1,3 +1,5 @@
|
|||
/target
|
||||
db/password.txt
|
||||
.env
|
||||
images/*.png
|
||||
tmpimages/*.png
|
||||
|
|
|
@ -14,6 +14,7 @@ services:
|
|||
target: final
|
||||
ports:
|
||||
- 8000:8000
|
||||
user: "${uid}:${gid}"
|
||||
environment:
|
||||
- ROCKET_DATABASES={fossil_postgres={url="postgres://postgres:$POSTGRES_PASSWORD@db/$POSTGRES_DB", max_connections=10, connect_timeout=10}}
|
||||
|
||||
|
@ -32,6 +33,9 @@ services:
|
|||
- type: bind
|
||||
source: ./images
|
||||
target: /srv/images
|
||||
- type: bind
|
||||
source: ./tmpimages
|
||||
target: /srv/tmpimages
|
||||
# The commented out section below is an example of how to define a PostgreSQL
|
||||
# database that your application can use. `depends_on` tells Docker Compose to
|
||||
# start the database before your application. The `db-data` volume persists the
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
GET /images/uuid.filetype
|
||||
GET /images/uuid.filetype DONE
|
||||
-> returns the image with the correct filetype - uuid.png would return the image in png format, etc
|
||||
|
||||
GET /images/by-user {user: "username"}
|
||||
-> gets all of the images made by {user}, if you are {user}, unless you have admin.
|
||||
GET /images/by-user/username DONE
|
||||
-> gets all of the images made by {username}, if you are {username}, or if you have admin.
|
||||
|
||||
POST /images/create {image: "image data"}
|
||||
POST /images/create {image: "image data"} DONE
|
||||
-> returns the uuid of the image, which it saves to the folder and database
|
||||
|
||||
DELETE /images/uuid
|
||||
DELETE /images/uuid DONE
|
||||
-> if you're the owner of the image or an admin, deletes the image. returns basic success/faliure
|
||||
|
||||
images are stored in /images/uuid.png. Image::get_by_uuid(uuid) just gets the image from the folder, verifies that it is an image, and returns it. Image::get_by_username(username) gets all database images from the database and returns the uuids. the client is responsible for getting the images.
|
||||
|
|
160
src/main.rs
160
src/main.rs
|
@ -5,11 +5,11 @@ use rocket::fs::{FileServer, NamedFile};
|
|||
use rocket::http::Status;
|
||||
use rocket::response::content::{self, RawHtml};
|
||||
use rocket::serde::Serialize;
|
||||
use rocket::tokio::io::AsyncReadExt;
|
||||
use rocket::{Build, Request, Rocket};
|
||||
use rocket_db_pools::sqlx::pool::PoolConnection;
|
||||
use rocket_db_pools::sqlx::Postgres;
|
||||
use rocket_db_pools::Connection;
|
||||
use uuid::Uuid;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
#[macro_use]
|
||||
|
@ -24,7 +24,7 @@ use rocket::serde::{json::Json, Deserialize};
|
|||
use rocket::http::CookieJar;
|
||||
use rocket::fs::TempFile;
|
||||
|
||||
use fossil::tables::{Db, Post, User};
|
||||
use fossil::tables::{Db, Post, User, Image};
|
||||
|
||||
#[get("/login")]
|
||||
fn login_page() -> RawHtml<String> {
|
||||
|
@ -36,6 +36,11 @@ fn createuser_page() -> RawHtml<String> {
|
|||
RawHtml(fs::read_to_string("/srv/web/createuser.html").unwrap())
|
||||
}
|
||||
|
||||
#[get("/uploadimage")]
|
||||
fn uploadimage() -> RawHtml<String> {
|
||||
RawHtml(fs::read_to_string("/srv/web/uploadimage.html").unwrap())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
struct LoginInfo {
|
||||
|
@ -277,11 +282,13 @@ async fn toggleperms(
|
|||
}
|
||||
|
||||
#[get("/images/<image>")]
|
||||
async fn get_image(image: String) -> Result<NamedFile, Status> {
|
||||
async fn get_image(image: String, mut db: Connection<Db>) -> Result<NamedFile, Status> {
|
||||
let mut split = image.split('.').collect::<Vec<&str>>();
|
||||
let format = split.pop().unwrap();
|
||||
let image = split.join(".");
|
||||
match Reader::open(format!("/srv/images/{image}.png")) {
|
||||
|
||||
match Image::get_by_uuid(&mut db, &image).await {
|
||||
Ok(_) => {match Reader::open(format!("/srv/images/{image}.png")) {
|
||||
Ok(i) => {
|
||||
match i.decode() {
|
||||
Ok(img) => {
|
||||
|
@ -309,21 +316,148 @@ async fn get_image(image: String) -> Result<NamedFile, Status> {
|
|||
}
|
||||
},
|
||||
Err(_) => Err(Status::NotFound)
|
||||
}},
|
||||
Err(_) => Err(Status::ImATeapot)
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/upload", format = "plain", data = "<file>")]
|
||||
async fn upload(mut file: TempFile<'_>) -> String {
|
||||
eprintln!("{file:?}");
|
||||
let mut content: String = String::new();
|
||||
file.open().await.unwrap().read_to_string(&mut content).await.unwrap();
|
||||
eprintln!("{content}");
|
||||
match file.copy_to("/srv/images/file.txt").await {
|
||||
Ok(_) => String::from("worked"),
|
||||
#[post("/upload", format = "image/png", data = "<file>")]
|
||||
async fn upload(mut file: TempFile<'_>, cookies: &CookieJar<'_>, mut db: Connection<Db>) -> String {
|
||||
let uuid = Uuid::new_v4().to_string();
|
||||
match file.copy_to(format!("/srv/tmpimages/{uuid}.png")).await {
|
||||
Ok(_) => {
|
||||
// validate that it is, in fact, an image
|
||||
match Reader::open(format!("/srv/tmpimages/{uuid}.png")) {
|
||||
Ok(_) => {
|
||||
match cookies.get_private("token") {
|
||||
Some(t) => {
|
||||
match User::get_by_token(&mut db, t).await {
|
||||
Some(user) => {
|
||||
if user.make_posts == true || user.admin == true {
|
||||
// upload to db
|
||||
match Image::create(&mut db, &uuid, user).await.status {
|
||||
StatusTypes::Success => {
|
||||
// move image
|
||||
match file.copy_to(format!("/srv/images/{uuid}.png")).await {
|
||||
Ok(_) => {
|
||||
match fs::remove_file(format!("/srv/tmpimages/{uuid}.png")) {
|
||||
Ok(_) => {
|
||||
uuid
|
||||
},
|
||||
Err(why) => {
|
||||
eprintln!("{why:?}");
|
||||
"couldn't delete old file".to_string()
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(why) => {
|
||||
eprintln!("{why:?}");
|
||||
"couldn't save file to final destination".to_string()
|
||||
}
|
||||
}
|
||||
},
|
||||
StatusTypes::Faliure => {
|
||||
"Couldn't save to DB".to_string()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
"Invalid perms".to_string()
|
||||
}
|
||||
},
|
||||
None => {
|
||||
"Invalid login token".to_string()
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
"Not logged in".to_string()
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(why) => {
|
||||
// isn't an image, or something else went wrong
|
||||
eprintln!("{why:?}");
|
||||
"error".to_string()
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(why) => why.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[delete("/images/<uuid>")]
|
||||
pub async fn delete_image(mut db: Connection<Db>, cookies: &CookieJar<'_>, uuid: String) -> String {
|
||||
let token = cookies.get_private("token");
|
||||
match token {
|
||||
Some(t) => {
|
||||
match User::get_by_token(&mut db, t).await {
|
||||
Some(user) => {
|
||||
match Image::is_owned_by(&mut db, &uuid, &user.username).await {
|
||||
Ok(b) => {
|
||||
match b {
|
||||
true => {
|
||||
match Image::delete(&mut db, &uuid).await.status {
|
||||
StatusTypes::Success => {
|
||||
match fs::remove_file(format!("/srv/images/{uuid}.png")) {
|
||||
Ok(_) => {
|
||||
"deleted!".to_string()
|
||||
},
|
||||
Err(why) => {
|
||||
eprintln!("{why:?}");
|
||||
"Image deleted from database but not filesystem".to_string()
|
||||
}
|
||||
}
|
||||
},
|
||||
StatusTypes::Faliure => "Couldn't delete image".to_string()
|
||||
}
|
||||
},
|
||||
false => "You don't own that image".to_string()
|
||||
}
|
||||
},
|
||||
Err(_) => "Couldn't get image".to_string()
|
||||
}
|
||||
},
|
||||
None => "Invalid login token".to_string()
|
||||
}
|
||||
},
|
||||
None => {
|
||||
"Not logged in".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/images/by-user/<username>")]
|
||||
pub async fn get_images_by_username(mut db: Connection<Db>, cookies: &CookieJar<'_>, username: String) -> Result<Json<Vec<String>>, String> {
|
||||
let token = cookies.get_private("token");
|
||||
match token {
|
||||
Some(t) => {
|
||||
match User::get_by_token(&mut db, t).await {
|
||||
Some(user) => {
|
||||
match user.admin || user.username == username {
|
||||
true => {
|
||||
match Image::get_by_username(&mut db, &username).await {
|
||||
Ok(images) => {
|
||||
Ok(Json::from(images.into_iter().map(|i| i.uuid).collect::<Vec<String>>()))
|
||||
},
|
||||
Err(why) => {
|
||||
eprintln!("{why:?}");
|
||||
Err("Couldn't get that user's images".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
false => Err("You don't have permission to do this".to_string())
|
||||
}
|
||||
},
|
||||
None => Err("Invalid login token".to_string())
|
||||
}
|
||||
},
|
||||
None => {
|
||||
Err("Not logged in".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[catch(default)]
|
||||
fn default_catcher(status: Status, _: &Request) -> RawHtml<String> {
|
||||
content::RawHtml(
|
||||
|
@ -426,7 +560,7 @@ use rocket::http::Method;
|
|||
.attach(cors.to_cors().unwrap())
|
||||
.mount(
|
||||
"/",
|
||||
routes![login_page, login, logout, createuser, createuser_page, account, adminpanel, toggleperms, get_image, upload],
|
||||
routes![login_page, login, logout, createuser, createuser_page, account, adminpanel, toggleperms, get_image, upload, uploadimage, delete_image, get_images_by_username],
|
||||
)
|
||||
.mount("/api", routes![api_perms])
|
||||
.mount("/css", FileServer::from("/srv/web/css"))
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use base64::{engine::general_purpose, Engine};
|
||||
use rand::{RngCore, SeedableRng};
|
||||
use regex::Regex;
|
||||
use rocket::fs::TempFile;
|
||||
use rocket::http::Cookie;
|
||||
use rocket_db_pools::sqlx::Executor;
|
||||
use rocket_db_pools::Connection;
|
||||
|
@ -240,17 +239,13 @@ pub struct Image {
|
|||
}
|
||||
|
||||
impl Image {
|
||||
pub async fn create(db: &mut Connection<Db>, mut image: TempFile<'_>, user: User) -> Status<String> {
|
||||
let uuid = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
image.persist_to(format!("/images/{uuid}.png")).await.unwrap();
|
||||
|
||||
pub async fn create(db: &mut Connection<Db>, uuid: &String, user: User) -> Status<String> {
|
||||
match db
|
||||
.execute(
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO images (uuid, owner_name)
|
||||
VALUES ($1, $2,);
|
||||
VALUES ($1, $2);
|
||||
"#,
|
||||
)
|
||||
.bind(&uuid)
|
||||
|
@ -260,7 +255,7 @@ impl Image {
|
|||
{
|
||||
Ok(_) => Status {
|
||||
status: StatusTypes::Success,
|
||||
data: uuid
|
||||
data: "useless field !".to_string()
|
||||
},
|
||||
Err(why) => {
|
||||
eprintln!("Couldn't create database entry: {why:?}");
|
||||
|
@ -284,6 +279,17 @@ impl Image {
|
|||
Err(_) => Err("Couldn't get image.".to_string()),
|
||||
}
|
||||
}
|
||||
pub async fn is_owned_by(db: &mut Connection<Db>, uuid: &String, username: &String) -> Result<bool, String> {
|
||||
match Image::get_by_uuid(db, &uuid).await {
|
||||
Ok(img) => {
|
||||
Ok(&img.owner_name == username)
|
||||
},
|
||||
Err(why) => {
|
||||
eprintln!("{why:?}");
|
||||
Err("couldn't get image".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn get_by_username(db: &mut Connection<Db>, owner_name: &String) -> Result<Vec<Image>, String> {
|
||||
match db
|
||||
.fetch_all(sqlx::query("SELECT * FROM images WHERE owner_name = $1;").bind(owner_name))
|
||||
|
|
Loading…
Reference in New Issue