Initial API setup
Right now we are just listing files in a given subdirectory with not authentication.
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
1738
Cargo.lock
generated
Normal file
1738
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "image-api"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Cameron Cordes <cameronc.dev@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "2.0"
|
||||||
|
actix-rt = "1.0"
|
||||||
|
futures = "0.3.5"
|
||||||
|
jsonwebtoken = "7.2.0"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_json = "1.0"
|
||||||
70
src/data/mod.rs
Normal file
70
src/data/mod.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
use actix_web::error::ErrorUnauthorized;
|
||||||
|
use actix_web::{dev, http::header, Error, FromRequest, HttpRequest};
|
||||||
|
use futures::future::{err, ok, Ready};
|
||||||
|
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Claims {
|
||||||
|
pub sub: String,
|
||||||
|
pub exp: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Claims {
|
||||||
|
type Err = jsonwebtoken::errors::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
println!("Parsing token: {}", s);
|
||||||
|
|
||||||
|
let claims = match decode::<Claims>(
|
||||||
|
s,
|
||||||
|
&DecodingKey::from_secret("secret_token".as_ref()),
|
||||||
|
&Validation::new(Algorithm::HS256),
|
||||||
|
) {
|
||||||
|
Ok(data) => Ok(data.claims),
|
||||||
|
Err(other) => Err(other),
|
||||||
|
};
|
||||||
|
|
||||||
|
return claims;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRequest for Claims {
|
||||||
|
type Error = Error;
|
||||||
|
type Future = Ready<Result<Self, Self::Error>>;
|
||||||
|
type Config = ();
|
||||||
|
|
||||||
|
fn from_request(req: &HttpRequest, _payload: &mut dev::Payload) -> Self::Future {
|
||||||
|
let claims = match req.headers().get(header::AUTHORIZATION) {
|
||||||
|
Some(header) => Claims::from_str(header.to_str().unwrap_or_else(|_| "")),
|
||||||
|
None => Err(jsonwebtoken::errors::Error::from(
|
||||||
|
jsonwebtoken::errors::ErrorKind::InvalidToken,
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(claims) = claims {
|
||||||
|
ok(claims)
|
||||||
|
} else {
|
||||||
|
err(ErrorUnauthorized("Bad token"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ThumbnailRequest {
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct LoginRequest {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct CreateAccountRequest {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
pub confirmation: String,
|
||||||
|
}
|
||||||
33
src/files.rs
Normal file
33
src/files.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::fs::read_dir;
|
||||||
|
use std::io;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub fn list_files(dir: PathBuf) -> io::Result<Vec<PathBuf>> {
|
||||||
|
let files = read_dir(dir)?
|
||||||
|
.map(|res| res.unwrap())
|
||||||
|
.filter(|entry| is_image_or_video(&entry.path()) || entry.file_type().unwrap().is_dir())
|
||||||
|
.map(|entry| entry.path())
|
||||||
|
.map(|path: PathBuf| {
|
||||||
|
let relative = path.strip_prefix(std::env::current_dir().unwrap()).unwrap();
|
||||||
|
relative.to_path_buf()
|
||||||
|
})
|
||||||
|
.collect::<Vec<PathBuf>>();
|
||||||
|
|
||||||
|
Ok(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_image_or_video(path: &Path) -> bool {
|
||||||
|
let extension = &path
|
||||||
|
.extension()
|
||||||
|
.unwrap_or_else(|| OsStr::new(""))
|
||||||
|
.to_str()
|
||||||
|
.unwrap_or_else(|| "")
|
||||||
|
.to_lowercase();
|
||||||
|
|
||||||
|
return extension == &"png"
|
||||||
|
|| extension == &"jpg"
|
||||||
|
|| extension == &"jpeg"
|
||||||
|
|| extension == &"rs"
|
||||||
|
|| extension == &"mp4";
|
||||||
|
}
|
||||||
50
src/main.rs
Normal file
50
src/main.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use actix_web::web::{HttpResponse, Json};
|
||||||
|
use actix_web::{get, post, App, HttpServer, Responder};
|
||||||
|
use data::{LoginRequest, ThumbnailRequest};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::files::list_files;
|
||||||
|
|
||||||
|
mod data;
|
||||||
|
mod files;
|
||||||
|
|
||||||
|
#[post("/register")]
|
||||||
|
async fn register() -> impl Responder {
|
||||||
|
"".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/login")]
|
||||||
|
async fn login(_creds: Json<LoginRequest>) -> impl Responder {
|
||||||
|
"".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/photos")]
|
||||||
|
async fn list_photos(req: Json<ThumbnailRequest>) -> impl Responder {
|
||||||
|
println!("{}", req.path);
|
||||||
|
|
||||||
|
let path = &req.path;
|
||||||
|
|
||||||
|
match path {
|
||||||
|
path if path.contains("..") => HttpResponse::BadRequest().finish(),
|
||||||
|
|
||||||
|
path => {
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
if path.is_relative() {
|
||||||
|
let mut full_path = std::env::current_dir().unwrap();
|
||||||
|
full_path.push(path);
|
||||||
|
let files = list_files(full_path);
|
||||||
|
HttpResponse::Ok().json(files.unwrap_or_default())
|
||||||
|
} else {
|
||||||
|
HttpResponse::BadRequest().finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
HttpServer::new(|| App::new().service(login).service(list_photos))
|
||||||
|
.bind("127.0.0.1:8088")?
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user