Skip to content

Commit 02a880c

Browse files
authored
Merge pull request #3 from KekOnTheWorld/main
Implemented web hosting
2 parents f2a8aa9 + f84464a commit 02a880c

File tree

4 files changed

+92
-43
lines changed

4 files changed

+92
-43
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ tmp/
44
upload/
55
Cargo.lock
66
.vscode/
7-
uploadserver.wiki/
7+
uploadserver.wiki/
8+
web/

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,4 @@ If you have aditional ideas how to make this tool better please create a feature
7979
<br>
8080

8181
## Contributing
82-
More information [here](https://kotw.dev/uploadserver/CONTRIBUTE).
82+
More information [here](https://oss.kotw.dev/uploadserver/CONTRIBUTE).

default.env

+14-8
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,27 @@
22
port=8080
33

44
# Base path of the api.
5-
base=/
5+
api_base=/api/
66

7-
# Directory for temporary files. Must exist!
8-
tmp=tmp/
9-
10-
# Directory for uploaded files. Must exist!
11-
upload=upload/
7+
# Directories
8+
tmp_dir=tmp/ # Directory for temporary files. Must exist!
9+
upload_dir=upload/ # Directory for uploaded files. Must exist!
10+
web_dir=web/ # Optional, only relevant when web_host is set!
1211

1312
# Database url. Must be valid!
1413
DATABASE_URL=postgres://username:password@host:port/database
1514

1615
# Embed settings
1716
embed_description="File"
1817
embed_color="#ffffff"
19-
download_url="http://localhost:6942/d/"
18+
download_url="http://localhost:6942/api/d/"
2019

2120
# Chunk size in KiB
22-
chunk_size=2048
21+
chunk_size=2048
22+
23+
# Options for hosting the frontend
24+
web_host=true
25+
web_base=/
26+
27+
# This can be used to rewrite the embed route
28+
embed_route_base=/

src/main.rs

+75-33
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::io;
1111
use std::io::prelude::*;
1212
use std::collections::HashMap;
1313
use std::path::Path;
14+
use std::path::PathBuf;
1415
use std::sync::{Mutex};
1516

1617
use dotenv::dotenv;
@@ -42,9 +43,13 @@ use models::file;
4243

4344
struct UploadState {
4445
map: Mutex<HashMap<String, UploadEntry>>,
45-
tmp: String,
46-
upload: String,
46+
47+
tmp_dir: String,
48+
upload_dir: String,
49+
web_dir: String,
50+
4751
chunk_size: ByteUnit,
52+
4853
datapool: database::PgPool,
4954

5055
embed_description: String,
@@ -79,23 +84,23 @@ impl<'r, 'o: 'r, 'a, R: Responder<'r, 'o>> Responder<'r, 'o> for Advanced<R> {
7984
}
8085
}
8186

82-
//----- START OF ROUTE CODE -----
87+
//----- START OF API CODE -----
8388

8489
#[get("/")]
85-
fn index() -> (ContentType, &'static str) {
86-
(ContentType::HTML, "KekUpload api made by KekOnTheWorld! <a href='https://github.com/KekOnTheWorld/uploadserver/wiki/API-Documentation'>Docs</a>")
90+
fn api_index() -> (ContentType, &'static str) {
91+
(ContentType::HTML, "UploadServer api made by KekOnTheWorld! <a href='https://oss.kotw.dev/uploadserver/docs/API'>Docs</a>")
8792
}
8893

8994
#[post("/c/<ext>")]
90-
fn create(ext: String, state: &State<UploadState>) -> status::Custom<String> {
95+
fn api_create(ext: String, state: &State<UploadState>) -> status::Custom<String> {
9196
if ext.len() > 6 {
9297
return status::Custom(Status::BadRequest, "EXT_TOO_LONG".to_owned());
9398
}
9499

95100
let map = &mut state.map.lock().unwrap();
96101

97102
let id = random::random_b64(64);
98-
let file = File::create(state.tmp.clone() + &id).unwrap();
103+
let file = File::create(state.tmp_dir.clone() + &id).unwrap();
99104
let hasher = Sha1::new();
100105

101106
let entry = UploadEntry { file: file, ext, hasher };
@@ -108,7 +113,7 @@ fn create(ext: String, state: &State<UploadState>) -> status::Custom<String> {
108113
}
109114

110115
#[post("/u/<id>/<hash>", data = "<data>")]
111-
async fn upload(data: Data<'_>, id: String, hash: String, state: &State<UploadState>) -> io::Result<status::Custom<&'static str>> {
116+
async fn api_upload(data: Data<'_>, id: String, hash: String, state: &State<UploadState>) -> io::Result<status::Custom<&'static str>> {
112117
let bytes = data.open(state.chunk_size).into_bytes().await?.into_inner();
113118

114119
let map = &mut state.map.lock().unwrap();
@@ -133,9 +138,9 @@ async fn upload(data: Data<'_>, id: String, hash: String, state: &State<UploadSt
133138
}
134139

135140
#[post("/r/<id>")]
136-
async fn remove(id: String, state: &State<UploadState>) -> status::Custom<&'static str> {
141+
async fn api_remove(id: String, state: &State<UploadState>) -> status::Custom<&'static str> {
137142
let map = &mut state.map.lock().unwrap();
138-
let file_path = state.tmp.clone() + &id;
143+
let file_path = state.tmp_dir.clone() + &id;
139144
if fs::remove_file(file_path).is_ok() {
140145
map.remove(&id);
141146
return status::Custom(Status::Ok, "OK");
@@ -145,14 +150,14 @@ async fn remove(id: String, state: &State<UploadState>) -> status::Custom<&'stat
145150
}
146151

147152
#[post("/f/<id>/<hash>")]
148-
async fn finish(id: String, hash: String, state: &State<UploadState>) -> status::Custom<String> {
153+
async fn api_finish(id: String, hash: String, state: &State<UploadState>) -> status::Custom<String> {
149154
let map = &mut state.map.lock().unwrap();
150155
if let Some(entry) = map.get_mut(&id) {
151156
let file_hash = hex::encode(entry.hasher.clone().finalize());
152-
let file_path = state.tmp.clone() + &id;
157+
let file_path = state.tmp_dir.clone() + &id;
153158

154159
if file_hash.eq(&hash) {
155-
fs::rename(file_path, state.upload.clone() + &file_hash)
160+
fs::rename(file_path, state.upload_dir.clone() + &file_hash)
156161
.expect("File rename error!");
157162

158163
let nid = random_b64(6);
@@ -183,7 +188,7 @@ async fn finish(id: String, hash: String, state: &State<UploadState>) -> status:
183188
}
184189

185190
#[get("/d/<id>")]
186-
async fn download(id: String, state: &State<UploadState>) -> Advanced<String> {
191+
async fn api_download(id: String, state: &State<UploadState>) -> Advanced<String> {
187192
let hash;
188193
let ext;
189194

@@ -196,13 +201,13 @@ async fn download(id: String, state: &State<UploadState>) -> Advanced<String> {
196201

197202
let filename = hash.clone() + "." + ext.as_str();
198203

199-
let nf = NamedFile::open(Path::new(state.upload.as_str()).join(hash)).await.ok();
204+
let nf = NamedFile::open(Path::new(state.upload_dir.as_str()).join(hash)).await.ok();
200205

201206
return Advanced(Some(filename), nf, "Kekw".to_owned());
202207
}
203208

204209
#[get("/e/<id>")]
205-
async fn embed(id: String, state: &State<UploadState>) -> status::Custom<(ContentType, String)> {
210+
async fn api_embed(id: String, state: &State<UploadState>) -> status::Custom<(ContentType, String)> {
206211
if let Some(entry) = file::File::find(id, &state.datapool.get().expect("Error while connecting to database!")).first() {
207212
let filename = entry.hash.clone() + "." + entry.ext.as_str();
208213

@@ -244,7 +249,20 @@ async fn embed(id: String, state: &State<UploadState>) -> status::Custom<(Conten
244249
}
245250
}
246251

247-
//----- END OF ROUTE CODE -----
252+
//----- END OF API CODE -----
253+
254+
//----- START OF WEB CODE -----
255+
#[get("/")]
256+
async fn web_index(state: &State<UploadState>) -> Option<NamedFile> {
257+
NamedFile::open(Path::new(state.web_dir.as_str()).join("index.html")).await.ok()
258+
}
259+
260+
#[get("/<path..>")]
261+
async fn web_base(path: PathBuf, state: &State<UploadState>) -> Option<NamedFile> {
262+
NamedFile::open(Path::new(state.web_dir.as_str()).join(path)).await.ok()
263+
}
264+
265+
//----- END OF WEB CODE -----
248266

249267
fn clean_tmp(tmp: String) {
250268
let dir = tmp.as_str();
@@ -256,15 +274,21 @@ fn clean_tmp(tmp: String) {
256274
fn rocket() -> _ {
257275
dotenv().ok();
258276

259-
let base = env::var("base")
277+
let embed_route_base = env::var("embed_route_base")
260278
.unwrap_or("/".to_owned());
261279

262-
let tmp = env::var("tmp")
280+
let api_base = env::var("api_base")
281+
.unwrap_or("/api/".to_owned());
282+
283+
let tmp_dir = env::var("tmp_dir")
263284
.unwrap_or("tmp/".to_owned());
264285

265-
let upload = env::var("upload")
286+
let upload_dir = env::var("upload_dir")
266287
.unwrap_or("upload/".to_owned());
267288

289+
let web_dir = env::var("web_dir")
290+
.unwrap_or("web/".to_owned());
291+
268292
let chunk_size = ByteUnit::Kibibyte(env::var("chunk_size")
269293
.unwrap_or("2048".to_owned()).parse().unwrap_or(2048));
270294

@@ -294,7 +318,7 @@ fn rocket() -> _ {
294318
..Default::default()
295319
}.to_cors().unwrap();
296320

297-
clean_tmp(tmp.clone());
321+
clean_tmp(tmp_dir.clone());
298322

299323
database::establish_connection(env::var("DATABASE_URL").expect("Missing dburl in .env"));
300324

@@ -303,29 +327,47 @@ fn rocket() -> _ {
303327
.merge(("limits", limits))
304328
.merge(("port", port));
305329

306-
println!("http://localhost:{}{}", port, base);
330+
println!("API: http://localhost:{}{}", port, api_base);
307331

308-
rocket::custom(figment)
332+
let mut server = rocket::custom(figment)
309333
.manage(UploadState {
310334
map: Mutex::new(HashMap::new()),
311335

312-
tmp,
313-
upload,
336+
tmp_dir,
337+
upload_dir,
314338
chunk_size,
339+
web_dir,
315340
datapool,
316341

317342
embed_description,
318343
embed_color,
319344
download_url
320345
})
321346
.attach(cors)
322-
.mount(base, routes![
323-
index,
324-
create,
325-
upload,
326-
finish,
327-
embed,
328-
remove,
329-
download
347+
.mount(api_base, routes![
348+
api_index,
349+
api_create,
350+
api_upload,
351+
api_finish,
352+
api_embed,
353+
api_remove,
354+
api_download
330355
])
356+
.mount(embed_route_base, routes![
357+
api_embed
358+
]);
359+
360+
if env::var("web_host").is_ok() {
361+
let web_base = env::var("web_base")
362+
.unwrap_or("/".to_owned());
363+
364+
println!("Web: http://localhost:{}{}", port, web_base);
365+
366+
server = server.mount(web_base, routes![
367+
web_index,
368+
web_base
369+
]);
370+
}
371+
372+
server
331373
}

0 commit comments

Comments
 (0)