feat: stuff

This commit is contained in:
DJMrTV 2025-02-27 21:49:37 +01:00
commit 6d58fd47a1
11 changed files with 244 additions and 366 deletions

View file

@ -1,18 +1,21 @@
use chrono::NaiveDate;
use chrono::{Datelike, NaiveDate};
use rocket::{post, State};
use serde::{Deserialize, Serialize};
use crate::account::account::{generate_password, User};
use crate::error::Errors;
use crate::nnid::pid_distribution::next_pid;
use crate::Pool;
use crate::xml::{Xml, YesNoVal};
#[derive(Deserialize)]
struct Email{
pub struct Email{
address: Box<str>
}
#[derive(Deserialize, Serialize)]
struct Mii{
pub struct Mii{
name: Box<str>,
primary: YesNoVal,
data: Box<str>,
@ -20,7 +23,7 @@ struct Mii{
#[derive(Deserialize)]
#[serde(rename(serialize = "person"))]
struct AccountCreationData{
pub struct AccountCreationData{
birth_date: NaiveDate,
user_id: Box<str>,
password: Box<str>,
@ -28,21 +31,28 @@ struct AccountCreationData{
language: Box<str>,
tz_name: Box<str>,
email: Email,
mii: Mii,
gender: Box<str>,
marketing_flag: YesNoVal,
off_device_flag: YesNoVal,
region: i32
}
#[derive(Serialize)]
#[serde(rename(serialize = "person"))]
struct AccountCreationResponseData{
pub struct AccountCreationResponseData{
pid: i32
}
#[post("/v1/api/people", data="<data>")]
async fn create_account(database: &State<Pool>, data: Xml<AccountCreationData>) -> Result<Xml<AccountCreationResponseData>, Errors>{
pub async fn create_account(database: &State<Pool>, data: Xml<AccountCreationData>) -> Result<Xml<AccountCreationResponseData>, Option<Errors>>{
let database = database.inner();
// its fine to crash here if we cant get the next pid as that is in my opinion a dead state
// anyways as noone can register anymore, EVER
let pid = next_pid(database).await;
let AccountCreationData {
user_id,
password,
@ -52,22 +62,27 @@ async fn create_account(database: &State<Pool>, data: Xml<AccountCreationData>)
email: Email{
address
},
mii: Mii{
data,
..
},
marketing_flag,
gender,
region,
country,
off_device_flag,
..
} = *data;
} = data.0;
let password = generate_password(pid, &password).ok_or(None)?;
let new_account = sqlx::query("
INSERT INTO users.users (
sqlx::query!("
INSERT INTO users (
pid,
username,
password,
birthdate,
birthdate,
timezone,
email,
country,
@ -75,26 +90,34 @@ async fn create_account(database: &State<Pool>, data: Xml<AccountCreationData>)
marketing_allowed,
off_device_allowed,
region,
gender,
mii_data
) VALUES (
?,?,?,?,?,?,?,?,?,?
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13
)
");
",
pid,
user_id.as_ref(),
password,
birth_date,
tz_name.as_ref(),
address.as_ref(),
country.as_ref(),
language.as_ref(),
marketing_flag.0,
off_device_flag.0,
region,
gender.as_ref(),
data.as_ref()
).execute(database).await.unwrap();
let pid = connection.transaction::<_, diesel::result::Error, _>(|conn| Box::pin(async move{
use crate::schema::users::dsl::*;
diesel::insert_into(users)
.values(&new_account)
.returning(User::as_returning())
.get_result(conn)
.await?;
Ok(())
})).await;
Ok(
Xml(AccountCreationResponseData{
pid
})
)
}
#[cfg(test)]

View file

@ -3,4 +3,6 @@ pub mod agreements;
pub mod timezones;
pub mod person_exists;
pub mod email;
mod create_account;
pub mod create_account;
pub mod oauth;
mod pid_distribution;

View file

@ -0,0 +1,52 @@
use rocket::{post, FromForm, State};
use rocket::form::Form;
use serde::Deserialize;
use crate::account::account::User;
use crate::error::{Error, Errors};
use crate::Pool;
const ACCOUNT_ID_OR_PASSWORD_ERRORS: Errors = Errors{
error: &[
Error{
code: "0106",
message: "Invalid account ID or password"
}
]
};
const ACCOUNT_BANNED_ERRORS: Errors = Errors{
error: &[
Error{
code: "0122",
message: "Device has been banned by game server"
}
]
};
#[derive(FromForm)]
pub struct TokenRequestData<'a>{
grant_type: &'a str,
user_id: &'a str,
password: &'a str,
password_type: &'a str,
}
#[post("/v1/api/oauth20/access_token/generate", data="<data>")]
pub async fn generate_token(pool: &State<Pool>, data: Form<TokenRequestData<'_>>) -> Result<(), Option<Errors<'static>>>{
let pool = pool.inner();
let user = User::get_by_username(data.user_id, pool).await
.ok_or(Some(ACCOUNT_ID_OR_PASSWORD_ERRORS))?;
if !user.verify_hashed_password(&data.password).is_some_and(|v| v){
return Err(Some(ACCOUNT_ID_OR_PASSWORD_ERRORS));
}
if user.account_level < 0{
return Err(Some(ACCOUNT_BANNED_ERRORS));
}
Ok(())
}

1
src/nnid/oauth/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod generate_token;

View file

@ -4,16 +4,19 @@ use crate::error::{Error, Errors};
use crate::Pool;
use crate::xml::Xml;
#[get("/v1/api/people/<username>")]
pub async fn person_exists(database: &State<Pool>, username: &str) -> Result<(), Errors<'static>>{
let database = database.inner();
let exists: bool = sqlx::query_as!(
bool,
"SELECT EXISTS(SELECT 1 FROM users.users WHERE username = ? )",
let exists = sqlx::query!(
"SELECT EXISTS(SELECT 1 FROM users WHERE username = $1 ) as exists",
username
).fetch_one(database)
.await
.ok()
.map(|v| v.exists)
.flatten()
.unwrap_or(true);
if exists {

View file

@ -0,0 +1,29 @@
use crate::Pool;
pub async fn next_pid(pool: &Pool) -> i32{
loop {
let next_pid = sqlx::query!("SELECT nextval('pid_counter') as pid")
.fetch_one(pool)
.await
.expect("unable to get next pid")
.pid
.expect("unable to get next pid") as i32;
let already_exists = sqlx::query!(
"SELECT EXISTS(select 1 from users where pid = $1)",
next_pid
).fetch_one(pool)
.await
.ok()
.map(|v| v.exists)
.flatten()
.unwrap_or(true);
if !already_exists{
break next_pid;
}
}
}