initial commit

This commit is contained in:
Maple Nebel 2026-05-04 23:09:51 +02:00
commit a88e0e3bbb
6 changed files with 3109 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

2983
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

12
Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "multiiverse"
version = "0.1.0"
edition = "2024"
[dependencies]
actix-web = "4.13.0"
dotenvy = "0.15.7"
log = "0.4.29"
quick-xml = { version = "0.39.3", features = ["serialize"] }
serde = { version = "1.0.228", features = ["derive"] }
sqlx = { version = "0.8.6", features = ["runtime-tokio", "tls-native-tls", "postgres", "chrono", "ipnetwork"] }

54
src/discovery/mod.rs Normal file
View file

@ -0,0 +1,54 @@
use std::{
env,
sync::{Arc, LazyLock},
};
use actix_web::{Responder, get, web};
use serde::Serialize;
use sqlx::PgPool;
use crate::xml::Xml;
#[derive(Serialize)]
struct Endpoint {
host: &'static str,
api_host: &'static str,
portal_host: &'static str,
n3ds_host: &'static str,
}
#[derive(Serialize)]
#[serde(rename = "result")]
struct Result {
has_error: u32,
version: u32,
endpoint: Endpoint,
}
static HOST_ENDPOINT: LazyLock<String> =
LazyLock::new(|| env::var("HOST_ENDPOINT").expect("host endpoint address was not set"));
static API_HOST_ENDPOINT: LazyLock<String> =
LazyLock::new(|| env::var("API_HOST_ENDPOINT").expect("api host endpoint address was not set"));
static PORTAL_HOST_ENDPOINT: LazyLock<String> = LazyLock::new(|| {
env::var("PORTAL_HOST_ENDPOINT").expect("portal host endpoint address was not set")
});
static N3DS_HOST_ENDPOINT: LazyLock<String> = LazyLock::new(|| {
env::var("N3DS_HOST_ENDPOINT").expect("n3ds host endpoint address was not set")
});
#[get("/v1/endpoint")]
pub async fn discovery(_pool: web::Data<Arc<PgPool>>) -> impl Responder {
Xml(Result {
has_error: 0,
version: 1,
endpoint: Endpoint {
host: &HOST_ENDPOINT,
api_host: &API_HOST_ENDPOINT,
portal_host: &PORTAL_HOST_ENDPOINT,
n3ds_host: &N3DS_HOST_ENDPOINT,
},
})
}

30
src/main.rs Normal file
View file

@ -0,0 +1,30 @@
use std::{env, sync::Arc};
use actix_web::{App, HttpServer, web};
use dotenvy::dotenv;
use sqlx::postgres::PgPoolOptions;
mod discovery;
mod xml;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
dotenv().ok();
let act_database_url = env::var("DATABASE_URL").expect("account database url is not set");
let pool = Arc::new(
PgPoolOptions::new()
.max_connections(5)
.connect(&act_database_url)
.await
.expect("unable to create pool"),
);
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(pool.clone()))
.service(discovery::discovery)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

29
src/xml.rs Normal file
View file

@ -0,0 +1,29 @@
use std::result;
use actix_web::{HttpResponse, HttpResponseBuilder, Responder, body::BoxBody, http::StatusCode};
use log::error;
use quick_xml::{SeError, se::Serializer};
use serde::Serialize;
pub fn serialize_with_version(serializable: &impl Serialize) -> result::Result<String, SeError> {
let mut write_dest = "<?xml version=\"1.0\"?>".to_owned();
serializable.serialize(Serializer::new(&mut write_dest))?;
Ok(write_dest)
}
pub struct Xml<T: Serialize>(pub T);
impl<T: Serialize> Responder for Xml<T> {
type Body = BoxBody;
fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse<Self::Body> {
match serialize_with_version(&self.0) {
Ok(o) => HttpResponseBuilder::new(StatusCode::OK).body(o),
Err(e) => {
error!("error whilest serializing xml: {}", e);
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
}