diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 2c605af..4422bca 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -1,12 +1,16 @@ pub use sea_orm_migration::prelude::*; mod m20220101_000001_create_table; +mod m20230123_000947_skin_table; pub struct Migrator; #[async_trait::async_trait] impl MigratorTrait for Migrator { fn migrations() -> Vec> { - vec![Box::new(m20220101_000001_create_table::Migration)] + vec![ + Box::new(m20220101_000001_create_table::Migration), + Box::new(m20230123_000947_skin_table::Migration), + ] } } diff --git a/migration/src/m20230123_000947_skin_table.rs b/migration/src/m20230123_000947_skin_table.rs new file mode 100644 index 0000000..4d5b4d7 --- /dev/null +++ b/migration/src/m20230123_000947_skin_table.rs @@ -0,0 +1,61 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Property::Table) + .if_not_exists() + .col( + ColumnDef::new(Property::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Property::UserId).integer().not_null()) // TODO + .foreign_key( + ForeignKey::create() + .name("fk-property-user") + .from(Property::Table, Property::UserId) + .to(User::Table, User::Id) + ) + .col(ColumnDef::new(Property::Name).string().not_null()) + .col(ColumnDef::new(Property::Value).string().not_null()) + .col(ColumnDef::new(Property::Signature).string().null()) + .to_owned(), + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Property::Table).to_owned()) + .await?; + + Ok(()) + } +} + +#[derive(Iden)] +enum User { + Table, + Id, +} + +#[derive(Iden)] +enum Property { + Table, + Id, + UserId, + Name, + Value, + Signature, +} diff --git a/src/entities/mod.rs b/src/entities/mod.rs index bd48d14..cb116a1 100644 --- a/src/entities/mod.rs +++ b/src/entities/mod.rs @@ -2,5 +2,6 @@ pub mod prelude; +pub mod property; pub mod token; pub mod user; diff --git a/src/entities/prelude.rs b/src/entities/prelude.rs index 397424d..afe7139 100644 --- a/src/entities/prelude.rs +++ b/src/entities/prelude.rs @@ -1,4 +1,5 @@ //! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.7 +pub use super::property::Entity as Property; pub use super::token::Entity as Token; pub use super::user::Entity as User; diff --git a/src/entities/property.rs b/src/entities/property.rs new file mode 100644 index 0000000..bc90b9a --- /dev/null +++ b/src/entities/property.rs @@ -0,0 +1,34 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.7 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "property")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub user_id: i32, + pub name: String, + pub value: String, + pub signature: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "NoAction" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/entities/token.rs b/src/entities/token.rs index 1dc1c70..e02ef6e 100644 --- a/src/entities/token.rs +++ b/src/entities/token.rs @@ -17,13 +17,19 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( - belongs_to = "Entity", + belongs_to = "super::user::Entity", from = "Column::UserId", - to = "Column::Id", + to = "super::user::Column::Id", on_update = "NoAction", on_delete = "NoAction" )] - SelfRef, + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } } impl ActiveModelBehavior for ActiveModel {} diff --git a/src/entities/user.rs b/src/entities/user.rs index ca278cf..9ad76f7 100644 --- a/src/entities/user.rs +++ b/src/entities/user.rs @@ -13,6 +13,23 @@ pub struct Model { } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} +pub enum Relation { + #[sea_orm(has_many = "super::property::Entity")] + Property, + #[sea_orm(has_many = "super::token::Entity")] + Token, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Property.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Token.def() + } +} impl ActiveModelBehavior for ActiveModel {} diff --git a/src/routes/register.rs b/src/routes/register.rs new file mode 100644 index 0000000..2c0210b --- /dev/null +++ b/src/routes/register.rs @@ -0,0 +1,83 @@ +use axum::{extract::State, Json}; +use reqwest::StatusCode; +use sea_orm::{EntityTrait, QueryFilter, ColumnTrait, Set, ActiveValue::NotSet}; +use tracing::info; + +use crate::{AppState, proto::{self, Response}, entities}; + +//TODO: replace format! with axum's own +pub async fn register_unmigrated(State(state): State, Json(payload): Json) -> Response { + info!(target: "REGISTER", "[UNMIGRATED] called with {:?}", payload); + + let form = proto::RefreshRequest { + accessToken: payload.token.accessToken, + clientToken: payload.token.clientToken, + selectedProfile: None, + requestUser: Some(true), + }; + + let c = reqwest::Client::new(); + let response = c.post("https://authserver.mojang.com/refresh") + .json(&form) + .send().await + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, Json(proto::Error::simple(format!("mojang error : {:?}", e)))))? + .text().await.expect("invalid body on response"); + + info!(target:"REGISTER", "Mojang response to refresh request: {}", &response); + let doc = serde_json::from_str::(&response) + .map_err(|_| (StatusCode::UNAUTHORIZED, Json(proto::Error::simple("invalid token"))))?; + + let user = doc.user.expect("user not found in response, even though we requested it!"); + + entities::user::Entity::insert( + entities::user::ActiveModel { + id: NotSet, + name: Set(user.username.clone()), + password: Set(payload.password), + uuid: Set(user.id), + } + ).exec(&state.db).await + .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json(proto::Error::simple("db error"))))?; + + let u = entities::user::Entity::find().filter( + entities::user::Column::Uuid.eq(user.id) + ).one(&state.db).await + .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json(proto::Error::simple("db error"))))? + .ok_or((StatusCode::INTERNAL_SERVER_ERROR, Json(proto::Error::simple("failed creating user"))))?; + + let response = c.get(format!("https://sessionserver.mojang.com/session/minecraft/profile/{}", user.id)) //TODO: needs trimmed uuid, is it trimmed by default? + .send().await + .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json(proto::Error::simple("internal server error"))))? + .text().await.expect("invalid body on response"); + + info!(target:"REGISTER", "Mojang response to texture fetch: {}", &response); + let doc = serde_json::from_str::(&response) + .map_err(|_| (StatusCode::UNAUTHORIZED, Json(proto::Error::simple("invalid texture response"))))?; + + let mut skin = proto::Property::default_skin(); + + for s in doc.properties.expect("missing properties field") { + if s.name == "textures" { + skin = s; + break; + } + } + + entities::property::Entity::insert( + entities::property::ActiveModel { + id: NotSet, + name: Set("textures".into()), + user_id: Set(u.id), + value: Set(skin.value), + signature: Set(skin.signature), + } + ).exec(&state.db).await + .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json(proto::Error::simple("db error"))))?; + + Ok(Json(proto::RegisterResponse { + user: proto::Profile { + name: user.username, + id: user.id, + } + })) +}