// trifid-api, an open source reimplementation of the Defined Networking nebula management server. // Copyright (C) 2023 c0repwn3r // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . use rocket::{post, State}; use rocket::serde::json::Json; use serde::{Serialize, Deserialize}; use std::time::{SystemTime, UNIX_EPOCH}; use rocket::http::{ContentType, Status}; use sqlx::PgPool; use crate::config::TFConfig; use crate::tokens::send_magic_link; use rocket::options; #[derive(Serialize, Deserialize)] #[serde(crate = "rocket::serde")] pub struct SignupRequest { pub email: String, } #[derive(Serialize, Deserialize)] #[serde(crate = "rocket::serde")] pub struct SignupResponseMetadata {} #[derive(Serialize, Deserialize)] #[serde(crate = "rocket::serde")] pub struct SignupResponse { pub data: Option, pub metadata: SignupResponseMetadata, } /* created_on TIMESTAMP NOT NULL, banned INTEGER NOT NULL, ban_reason VARCHAR(1024) NOT NULL */ #[options("/v1/signup")] pub async fn options() -> &'static str { "" } #[post("/v1/signup", data = "")] pub async fn signup_request(req: Json, pool: &State, config: &State) -> Result<(ContentType, Json), (Status, String)> { // figure out if the user already exists let mut id = -1; match sqlx::query!("SELECT id FROM users WHERE email = $1", req.email.clone()).fetch_optional(pool.inner()).await { Ok(res) => if let Some(r) = res { id = r.id as i64 }, Err(e) => { return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e))) } } if id == -1 { let id_res = match sqlx::query!("INSERT INTO users (email, created_on, banned, ban_reason, totp_secret, totp_otpurl, totp_verified) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT DO NOTHING RETURNING id;", req.email, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64, 0, "", "", "", 0).fetch_one(pool.inner()).await { Ok(row) => row.id, Err(e) => { return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e))) } }; id = id_res as i64; } // send magic link to email match send_magic_link(id, req.email.clone(), pool.inner(), config.inner()).await { Ok(_) => (), Err(e) => { return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e))) } }; // this endpoint doesn't actually ever return an error? it will send you the magic link no matter what // this appears to do the exact same thing as /v1/auth/magic-link, but it doesn't check if you have an account (magic-link does) Ok((ContentType::JSON, Json(SignupResponse { data: None, metadata: SignupResponseMetadata {}, }))) }