1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
use std::collections::HashMap;
use anyhow::Context;
use axum::body::Bytes;
use axum::extract::{Multipart, Request, State};
use axum::http::{HeaderMap, StatusCode};
use axum::middleware::Next;
use axum::response::{IntoResponse, Response};
use axum::{Extension, Json};
use rand::Rng;
use subtle::ConstantTimeEq;
use crate::db::story::Story;
use crate::db::ProductDatabaseHandler;
use crate::error::AppError;
use super::{ApiContext, AppState};
pub fn generate_token() -> String {
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789)(*&^%$#@!~";
let mut rng = rand::thread_rng();
let token: String = (0..32)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect();
println!("New Token generated: {}", token);
token
}
pub async fn admin_middleware(
State(state): State<AppState>,
headers: HeaderMap,
request: Request,
next: Next,
) -> Result<Response, StatusCode> {
let mut expected_token = state.admin_token.lock().await;
let token = headers
.get("X-Admin-Token")
.and_then(|header| header.to_str().ok())
.ok_or(StatusCode::UNAUTHORIZED)?;
if expected_token
.as_bytes()
.ct_ne(token.as_bytes())
.unwrap_u8()
== 1
{
eprintln!("Warning: Unauthorized access attempt with invalid token");
*expected_token = generate_token();
return Err(StatusCode::UNAUTHORIZED);
}
*expected_token = generate_token();
Ok(next.run(request).await)
}
pub async fn admin_healthcheck() -> impl IntoResponse {
StatusCode::OK
}
pub async fn upload_story(
ctx: Extension<ApiContext>,
mut multipart: Multipart,
) -> Result<impl IntoResponse, AppError> {
let mut form: HashMap<String, Bytes> = HashMap::new();
while let Some(field) = multipart.next_field().await? {
if let Some(label) = field.name() {
form.insert(label.to_string(), field.bytes().await?);
}
}
let name = if let Some(name_value) = form.get("name") {
String::from_utf8(name_value.to_vec()).with_context(|| "Failed to parse name")?
} else {
return Ok(StatusCode::BAD_REQUEST.into_response());
};
let description = if let Some(description_value) = form.get("description") {
String::from_utf8(description_value.to_vec())
.with_context(|| "Failed to parse description")?
} else {
return Ok(StatusCode::BAD_REQUEST.into_response());
};
let language = if let Some(language_value) = form.get("language") {
String::from_utf8(language_value.to_vec()).with_context(|| "Failed to parse language")?
} else {
return Ok(StatusCode::BAD_REQUEST.into_response());
};
let pdf = if let Some(pdf_value) = form.get("pdf") {
pdf_value.to_vec()
} else {
return Ok(StatusCode::BAD_REQUEST.into_response());
};
let epub = if let Some(epub_value) = form.get("epub") {
epub_value.to_vec()
} else {
return Ok(StatusCode::BAD_REQUEST.into_response());
};
let story_template = Story {
id: 0,
pid: 0,
language,
pdf,
epub,
};
let story = story_template.post(&ctx.pool, name, description).await?;
Ok((StatusCode::CREATED, Json(story)).into_response())
}
|