Initial Commit
This commit is contained in:
commit
c09b00ff35
30 changed files with 1930 additions and 0 deletions
3
src/services/mod.rs
Normal file
3
src/services/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod nppl;
|
||||
pub mod npts;
|
||||
pub mod npdi;
|
||||
56
src/services/npdi.rs
Normal file
56
src/services/npdi.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use std::io::Cursor;
|
||||
use rocket::http::{Header, Status};
|
||||
use rocket::{Data, State};
|
||||
use rocket::response::{Response, Responder};
|
||||
use crate::Pool;
|
||||
use crate::database::get_wup_task_file_by_data_id;
|
||||
|
||||
#[derive(Responder)]
|
||||
pub struct DataResponder<T> {
|
||||
inner: T,
|
||||
content_type_header: Header<'static>,
|
||||
content_disposition_header: Header<'static>,
|
||||
content_transfer_encoding_header: Header<'static>,
|
||||
content_length_header: Header<'static>,
|
||||
}
|
||||
|
||||
impl<'r, 'o: 'r, T: Responder<'r, 'o>> DataResponder<T> {
|
||||
fn new(inner: T, content_length: String) -> Self {
|
||||
DataResponder {
|
||||
inner,
|
||||
content_type_header: Header::new("Content-Type", "applicatoin/octet-stream"), // Intentional spelling mistake
|
||||
content_disposition_header: Header::new("Content-Disposition", "attachment"),
|
||||
content_transfer_encoding_header: Header::new("Content-Transfer-Encoding", "binary"),
|
||||
content_length_header: Header::new("Content-Length", content_length),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::get("/p01/data/1/<boss_app_id>/<data_id>/<file_hash>")]
|
||||
pub async fn data(pool: &State<Pool>, boss_app_id: String, data_id: i64, file_hash: String) -> Result<DataResponder<Vec<u8>>, Status> {
|
||||
let pool = pool.inner();
|
||||
|
||||
let file_wup = get_wup_task_file_by_data_id(pool, data_id).await;
|
||||
|
||||
let file_wup = match file_wup {
|
||||
Some(file_wup) => file_wup,
|
||||
None => return Err(Status::NotFound),
|
||||
};
|
||||
|
||||
if file_wup.hash != file_hash || file_wup.boss_app_id != boss_app_id { return Err(Status::NotFound); }
|
||||
|
||||
let file = sqlx::query_scalar!(
|
||||
"SELECT data FROM files WHERE key = $1",
|
||||
file_wup.file_key,
|
||||
)
|
||||
.fetch_optional(pool)
|
||||
.await;
|
||||
|
||||
let file = match file {
|
||||
Ok(Some(file)) => { file },
|
||||
Ok(None) => return Err(Status::NotFound),
|
||||
Err(_) => return Err(Status::NotFound),
|
||||
};
|
||||
|
||||
Ok(DataResponder::new(file, file_wup.size.to_string()))
|
||||
}
|
||||
241
src/services/nppl.rs
Normal file
241
src/services/nppl.rs
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
use rocket::http::Status;
|
||||
use serde::Serialize;
|
||||
use chrono::Utc;
|
||||
|
||||
/* Commented out until database support is added
|
||||
use rocket::State;
|
||||
use crate::Pool;
|
||||
*/
|
||||
|
||||
// TODO:
|
||||
// - Use database for policy lists for easier modification if needed
|
||||
|
||||
#[derive(Serialize)]
|
||||
enum TaskLevel {
|
||||
STOPPED,
|
||||
HIGH,
|
||||
EXPEDITE,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct PolicyPriority {
|
||||
title_id: String,
|
||||
task_id: String,
|
||||
level: TaskLevel,
|
||||
persistent: Option<bool>,
|
||||
revive: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct PolicyList {
|
||||
major_version: i32,
|
||||
minor_version: i32,
|
||||
list_id: i32,
|
||||
default_stop: bool,
|
||||
force_version_up: bool,
|
||||
update_time: String,
|
||||
priority: Vec<PolicyPriority>,
|
||||
}
|
||||
|
||||
async fn get_policy_list(console_type: String, major_version: String, country_code: String) -> Result<String, Status> {
|
||||
|
||||
let policy_list: Option<PolicyList> = match console_type.as_str() {
|
||||
|
||||
"0" => { // 3DS
|
||||
if major_version != "3" { None }
|
||||
else {
|
||||
Some(PolicyList{
|
||||
major_version: 3,
|
||||
minor_version: 0,
|
||||
list_id: 1891,
|
||||
default_stop: false,
|
||||
force_version_up: false,
|
||||
update_time: Utc::now().format("%Y-%m-%dT%H:%M:%S+0000").to_string(),
|
||||
priority: vec![
|
||||
PolicyPriority{
|
||||
title_id: "0004003000008f02".to_string(),
|
||||
task_id: "basho0".to_string(),
|
||||
level: TaskLevel::HIGH,
|
||||
persistent: Some(true),
|
||||
revive: Some(true),
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000400300000bc00".to_string(),
|
||||
task_id: "OlvNotf".to_string(),
|
||||
level: TaskLevel::HIGH,
|
||||
persistent: Some(true),
|
||||
revive: Some(true),
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000400300000bd00".to_string(),
|
||||
task_id: "OlvNotf".to_string(),
|
||||
level: TaskLevel::HIGH,
|
||||
persistent: Some(true),
|
||||
revive: Some(true),
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000400300000be00".to_string(),
|
||||
task_id: "OlvNotf".to_string(),
|
||||
level: TaskLevel::HIGH,
|
||||
persistent: Some(true),
|
||||
revive: Some(true),
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "0004003000008f02".to_string(),
|
||||
task_id: "pl".to_string(),
|
||||
level: TaskLevel::HIGH,
|
||||
persistent: Some(true),
|
||||
revive: Some(true),
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "0004013000003400".to_string(),
|
||||
task_id: "sprelay".to_string(),
|
||||
level: TaskLevel::HIGH,
|
||||
persistent: Some(true),
|
||||
revive: Some(true),
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
"1" => { // Wii U
|
||||
if major_version != "1" { None }
|
||||
else {
|
||||
Some(PolicyList{
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
list_id: 1891,
|
||||
default_stop: false,
|
||||
force_version_up: false,
|
||||
update_time: Utc::now().format("%Y-%m-%dT%H:%M:%S+0000").to_string(),
|
||||
priority: vec![
|
||||
PolicyPriority{
|
||||
title_id: "0005003010016000".to_string(),
|
||||
task_id: "olvinfo".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "0005003010016100".to_string(),
|
||||
task_id: "olvinfo".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "0005003010016200".to_string(),
|
||||
task_id: "olvinfo".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000500301001600a".to_string(),
|
||||
task_id: "olv1".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000500301001610a".to_string(),
|
||||
task_id: "olv1".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000500301001620a".to_string(),
|
||||
task_id: "olv1".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "0005001010040000".to_string(),
|
||||
task_id: "olvtopic".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "0005001010040100".to_string(),
|
||||
task_id: "olvtopic".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "0005001010040200".to_string(),
|
||||
task_id: "olvtopic".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000500101005a000".to_string(),
|
||||
task_id: "Chat".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000500101005a100".to_string(),
|
||||
task_id: "Chat".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000500101005a200".to_string(),
|
||||
task_id: "Chat".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
PolicyPriority{
|
||||
title_id: "000500101004c100".to_string(),
|
||||
task_id: "plog".to_string(),
|
||||
level: TaskLevel::EXPEDITE,
|
||||
persistent: None,
|
||||
revive: None,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
_ => return Err(Status::InternalServerError),
|
||||
};
|
||||
|
||||
match policy_list {
|
||||
|
||||
Some(policy_list) => {
|
||||
|
||||
let xml = quick_xml::se::to_string(&policy_list);
|
||||
match xml {
|
||||
Ok(xml) => return Ok(xml),
|
||||
Err(_) => return Err(Status::InternalServerError),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
None => return Err(Status::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[rocket::get("/p01/policylist/<major_version>/<country_code>")]
|
||||
pub async fn policylist(major_version: String, country_code: String) -> Result<String, Status> {
|
||||
let console_type = "0".to_string();
|
||||
get_policy_list(console_type, major_version, country_code).await
|
||||
}
|
||||
|
||||
#[rocket::get("/p01/policylist/<console_type>/<major_version>/<country_code>")]
|
||||
pub async fn policylist_consoletype(console_type: String, major_version: String, country_code: String) -> Result<String, Status> {
|
||||
get_policy_list(console_type, major_version, country_code).await
|
||||
}
|
||||
202
src/services/npts.rs
Normal file
202
src/services/npts.rs
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
use std::env;
|
||||
use rocket::State;
|
||||
use rocket::form::FromForm;
|
||||
use rocket::http::Status;
|
||||
use rocket::response::content::RawXml;
|
||||
use serde::Serialize;
|
||||
use crate::Pool;
|
||||
use crate::database::{get_task, get_wup_task_files, get_wup_task_file};
|
||||
use crate::models::task::Task;
|
||||
use crate::models::file_wup::FileWUP;
|
||||
use crate::models::file_wup::FileWUPAttributes;
|
||||
|
||||
#[derive(FromForm)]
|
||||
struct QueryParams {
|
||||
c: Option<String>,
|
||||
l: Option<String>,
|
||||
mode: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct FileWUPNotify {
|
||||
new: String,
|
||||
led: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct FileWUPFormatted {
|
||||
filename: String,
|
||||
data_id: Option<i64>,
|
||||
r#type: String,
|
||||
url: Option<String>,
|
||||
size: i64,
|
||||
attributes: Option<FileWUPAttributes>,
|
||||
notify: Option<FileWUPNotify>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct TaskSheetFiles {
|
||||
file: Vec<FileWUPFormatted>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct TaskSheet {
|
||||
title_id: String,
|
||||
task_id: String,
|
||||
service_status: String,
|
||||
files: TaskSheetFiles,
|
||||
}
|
||||
|
||||
fn build_file(task: Task, file: &FileWUP, attributes_mode: bool, cdn_url: &String) -> FileWUPFormatted {
|
||||
let file = file.clone();
|
||||
if attributes_mode {
|
||||
FileWUPFormatted {
|
||||
filename: file.name,
|
||||
r#type: file.r#type,
|
||||
size: file.size,
|
||||
attributes: Some(FileWUPAttributes {
|
||||
attribute1: file.attributes.attribute1.clone(),
|
||||
attribute2: file.attributes.attribute2.clone(),
|
||||
attribute3: file.attributes.attribute3.clone(),
|
||||
description: file.attributes.description.clone(),
|
||||
}),
|
||||
data_id: None,
|
||||
url: None,
|
||||
notify: None,
|
||||
}
|
||||
} else {
|
||||
let url = format!("{}/p01/data/1/{}/{}/{}", cdn_url, task.boss_app_id, file.data_id.unwrap(), file.hash);
|
||||
FileWUPFormatted {
|
||||
filename: file.name,
|
||||
data_id: Some(file.data_id.unwrap()),
|
||||
r#type: file.r#type,
|
||||
url: Some(url),
|
||||
size: file.size,
|
||||
notify: Some(FileWUPNotify {
|
||||
new: file.notify_on_new.join(","),
|
||||
led: file.notify_led,
|
||||
}),
|
||||
attributes: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::get("/p01/tasksheet/<id>/<boss_app_id>/<task_id>?<query..>")]
|
||||
pub async fn tasksheet(pool: &State<Pool>, id: &str, boss_app_id: &str, task_id: &str, query: QueryParams) -> Result<RawXml<String>, Status> {
|
||||
let pool = pool.inner();
|
||||
let cdn_url = match env::var("BOSS_CDN_URL") {
|
||||
Ok(url) => url,
|
||||
Err(_) => return Err(Status::InternalServerError),
|
||||
};
|
||||
|
||||
let country = query.c;
|
||||
let language = query.l;
|
||||
let mode = query.mode;
|
||||
|
||||
let task = match get_task(pool, boss_app_id.to_string(), task_id.to_string()).await {
|
||||
Some(task) => task,
|
||||
None => return Err(Status::NotFound)
|
||||
};
|
||||
|
||||
let files = get_wup_task_files(pool, false, boss_app_id.to_string(), task_id.to_string(), country, language, None).await;
|
||||
|
||||
let tasksheet = TaskSheet{
|
||||
title_id: task.title_id.clone(),
|
||||
task_id: task.id.clone(),
|
||||
service_status: task.status.clone(),
|
||||
files: TaskSheetFiles{file: files.iter().map(|f| build_file(task.clone(), f, mode == Some("attr".to_string()), &cdn_url)).collect()},
|
||||
};
|
||||
|
||||
let xml = quick_xml::se::to_string(&tasksheet);
|
||||
|
||||
match xml {
|
||||
Ok(xml) => {
|
||||
let xml = format!(r#"<?xml version="1.0" encoding="UTF-8"?>{}"#, xml);
|
||||
Ok(RawXml(xml))
|
||||
},
|
||||
Err(_) => Err(Status::InternalServerError),
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::get("/p01/tasksheet/<id>/<task_id>?<query..>")]
|
||||
pub async fn tasksheet_no_boss_app_id(pool: &State<Pool>, id: &str, task_id: &str, query: QueryParams) -> Result<RawXml<String>, Status> {
|
||||
let pool = pool.inner();
|
||||
let cdn_url = match env::var("BOSS_CDN_URL") {
|
||||
Ok(url) => url,
|
||||
Err(_) => return Err(Status::InternalServerError),
|
||||
};
|
||||
|
||||
let country = query.c;
|
||||
let language = query.l;
|
||||
let mode = query.mode;
|
||||
|
||||
let boss_app_id = "";
|
||||
|
||||
let task = match get_task(pool, boss_app_id.to_string(), task_id.to_string()).await {
|
||||
Some(task) => task,
|
||||
None => return Err(Status::NotFound)
|
||||
};
|
||||
|
||||
let files = get_wup_task_files(pool, false, boss_app_id.to_string(), task_id.to_string(), country, language, None).await;
|
||||
|
||||
let tasksheet = TaskSheet{
|
||||
title_id: task.title_id.clone(),
|
||||
task_id: task.id.clone(),
|
||||
service_status: task.status.clone(),
|
||||
files: TaskSheetFiles{file: files.iter().map(|f| build_file(task.clone(), f, mode == Some("attr".to_string()), &cdn_url)).collect()},
|
||||
};
|
||||
|
||||
let xml = quick_xml::se::to_string(&tasksheet);
|
||||
|
||||
match xml {
|
||||
Ok(xml) => {
|
||||
let xml = format!(r#"<?xml version="1.0" encoding="UTF-8"?>{}"#, xml);
|
||||
Ok(RawXml(xml))
|
||||
},
|
||||
Err(_) => Err(Status::InternalServerError),
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::get("/p01/tasksheet/<id>/<boss_app_id>/<task_id>/<file_name>?<query..>")]
|
||||
pub async fn tasksheet_file(pool: &State<Pool>, id: &str, boss_app_id: &str, task_id: &str, file_name: &str, query: QueryParams) -> Result<RawXml<String>, Status> {
|
||||
let pool = pool.inner();
|
||||
let cdn_url = match env::var("BOSS_CDN_URL") {
|
||||
Ok(url) => url,
|
||||
Err(_) => return Err(Status::InternalServerError),
|
||||
};
|
||||
|
||||
let country = query.c;
|
||||
let language = query.l;
|
||||
let mode = query.mode;
|
||||
|
||||
let task = match get_task(pool, boss_app_id.to_string(), task_id.to_string()).await {
|
||||
Some(task) => task,
|
||||
None => return Err(Status::NotFound)
|
||||
};
|
||||
|
||||
let file = match get_wup_task_file(pool, false, boss_app_id.to_string(), task_id.to_string(), file_name.to_string(), country, language, None).await {
|
||||
Some(file) => file,
|
||||
None => return Err(Status::NotFound),
|
||||
};
|
||||
|
||||
let tasksheet = TaskSheet{
|
||||
title_id: task.title_id.clone(),
|
||||
task_id: task.id.clone(),
|
||||
service_status: task.status.clone(),
|
||||
files: TaskSheetFiles{file: vec![build_file(task.clone(), &file, mode == Some("attr".to_string()), &cdn_url)]},
|
||||
};
|
||||
|
||||
let xml = quick_xml::se::to_string(&tasksheet);
|
||||
|
||||
match xml {
|
||||
Ok(xml) => {
|
||||
let xml = format!(r#"<?xml version="1.0" encoding="UTF-8"?>{}"#, xml);
|
||||
Ok(RawXml(xml))
|
||||
},
|
||||
Err(_) => Err(Status::InternalServerError),
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue