feat: auth
This commit is contained in:
+1
-1
@@ -16,7 +16,7 @@ utoipa = { version = "5.3.1", features = ["axum_extras"] }
|
||||
utoipa-swagger-ui = { version = "9.0.0", features = ["axum"] }
|
||||
redis = "0.28.2"
|
||||
lettre = { version = "0.11.12", features = ["tokio1-native-tls"] }
|
||||
surrealdb = "2.2.1"
|
||||
surrealdb = { version = "2.2.1", features = ["protocol-http"] }
|
||||
thiserror = "2.0.11"
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -6,7 +6,6 @@ pub mod v2;
|
||||
|
||||
pub async fn apps(surrealdb: SurrealClient, redisdb: RedisClient) -> Router {
|
||||
let state = AppState { surrealdb, redisdb };
|
||||
|
||||
Router::new()
|
||||
.nest("/v1", v1::routes().await)
|
||||
.nest("/v2", v2::routes().await)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use axum::{response::IntoResponse, Extension, Json};
|
||||
|
||||
use super::{mutation_login, AuthLoginRequestDto};
|
||||
use super::{
|
||||
mutation_login, mutation_register, AuthLoginRequestDto, AuthRegisterRequestDto,
|
||||
};
|
||||
use crate::AppState;
|
||||
use axum::{response::IntoResponse, Extension, Json};
|
||||
|
||||
pub async fn post_login(
|
||||
Extension(state): Extension<AppState>,
|
||||
@@ -9,3 +10,10 @@ pub async fn post_login(
|
||||
) -> impl IntoResponse {
|
||||
mutation_login(payload, &state).await
|
||||
}
|
||||
|
||||
pub async fn post_register(
|
||||
Extension(state): Extension<AppState>,
|
||||
Json(payload): Json<AuthRegisterRequestDto>,
|
||||
) -> impl IntoResponse {
|
||||
mutation_register(payload, &state).await
|
||||
}
|
||||
|
||||
@@ -8,10 +8,14 @@ pub struct AuthLoginRequestDto {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct AuthRegisterRequestDto {
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
pub fullname: String,
|
||||
pub struct AuthLoginResponsetDto {
|
||||
pub token: TokenDto,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct TokenDto {
|
||||
pub access_token: String,
|
||||
pub refresh_token: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
|
||||
@@ -1,107 +1,32 @@
|
||||
use super::{auth_dto::AuthLoginRequestDto, AuthRegisterRequestDto};
|
||||
use crate::{
|
||||
common_response, hash_password, success_response, v1::UsersItemDto, AppState,
|
||||
ResponseSuccessDto,
|
||||
};
|
||||
use axum::{http::StatusCode, response::Response};
|
||||
use serde_json;
|
||||
use crate::{v1::UsersItemDto, AppState};
|
||||
use std::error::Error;
|
||||
|
||||
const USERS_KEY: &str = "users";
|
||||
use super::AuthRegisterRequestDto;
|
||||
|
||||
pub async fn mutation_login(
|
||||
params: AuthLoginRequestDto,
|
||||
pub async fn query_user_by_email(
|
||||
email: String,
|
||||
state: &AppState,
|
||||
) -> Response {
|
||||
let user: Option<AuthLoginRequestDto> = match state
|
||||
.surrealdb
|
||||
.select((USERS_KEY, params.email.as_str()))
|
||||
.await
|
||||
{
|
||||
Ok(user) => user,
|
||||
Err(err) => {
|
||||
return common_response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
&err.to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
) -> Result<UsersItemDto, Box<dyn Error>> {
|
||||
let db = &state.surrealdb;
|
||||
|
||||
let mut redis_conn = match state.redisdb.get_connection() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => {
|
||||
return common_response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
&err.to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
let mut result = db
|
||||
.query("SELECT * FROM app_users WHERE email = $email LIMIT 1;")
|
||||
.bind(("email", email.clone()))
|
||||
.await?;
|
||||
|
||||
let user_json = match serde_json::to_string(&user) {
|
||||
Ok(json) => json,
|
||||
Err(err) => {
|
||||
return common_response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
&err.to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
let user: Option<UsersItemDto> = result.take(0)?;
|
||||
|
||||
if let Err(err) = redis::cmd("SET")
|
||||
.arg("users_data")
|
||||
.arg(user_json)
|
||||
.query::<()>(&mut redis_conn)
|
||||
{
|
||||
return common_response(StatusCode::INTERNAL_SERVER_ERROR, &err.to_string());
|
||||
}
|
||||
|
||||
let response = ResponseSuccessDto { data: params };
|
||||
|
||||
success_response(response)
|
||||
user.ok_or_else(|| format!("User not found for email: {}", email).into())
|
||||
}
|
||||
|
||||
pub async fn mutation_register(
|
||||
params: AuthRegisterRequestDto,
|
||||
pub async fn query_create_user(
|
||||
data: AuthRegisterRequestDto,
|
||||
state: &AppState,
|
||||
) -> Response {
|
||||
let user_key = format!("{}:{}", USERS_KEY, params.email);
|
||||
) -> Result<String, Box<dyn Error>> {
|
||||
let db = &state.surrealdb;
|
||||
|
||||
let existing_user: Option<UsersItemDto> =
|
||||
match state.surrealdb.select(&user_key).await {
|
||||
Ok(user) => user,
|
||||
Err(err) => {
|
||||
return common_response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
&err.to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
let _record: Option<UsersItemDto> =
|
||||
db.create(("app_users", &data.email)).content(data).await?;
|
||||
|
||||
if existing_user.is_some() {
|
||||
return common_response(StatusCode::CONFLICT, "User already exists");
|
||||
}
|
||||
|
||||
let hashed_password = hash_password(¶ms.password);
|
||||
|
||||
let created_user: AuthLoginRequestDto =
|
||||
match state.surrealdb.create(&user_key, ¶ms).await {
|
||||
Ok(user) => Some(user),
|
||||
Err(err) => {
|
||||
return common_response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
&err.to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let user_json = match serde_json::to_string(&created_user) {
|
||||
Ok(json) => json,
|
||||
Err(err) => {
|
||||
return common_response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
&err.to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
common_response(StatusCode::CREATED, "Success Register User")
|
||||
Ok("Success create user".into())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
use axum::{http::StatusCode, response::Response};
|
||||
|
||||
use super::{
|
||||
query_create_user, query_user_by_email, AuthLoginRequestDto,
|
||||
AuthRegisterRequestDto,
|
||||
};
|
||||
use crate::{common_response, hash_password, verify_password, AppState};
|
||||
|
||||
pub async fn mutation_login(
|
||||
payload: AuthLoginRequestDto,
|
||||
state: &AppState,
|
||||
) -> Response {
|
||||
match query_user_by_email(payload.email, state).await {
|
||||
Ok(user) => {
|
||||
let is_password_correct =
|
||||
verify_password(&payload.password, &user.password).unwrap_or(false);
|
||||
|
||||
if is_password_correct {
|
||||
common_response(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"Email or password not correct",
|
||||
);
|
||||
}
|
||||
|
||||
common_response(StatusCode::OK, "Success Login")
|
||||
}
|
||||
Err(err) => common_response(StatusCode::UNAUTHORIZED, &err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mutation_register(
|
||||
payload: AuthRegisterRequestDto,
|
||||
state: &AppState,
|
||||
) -> Response {
|
||||
if query_user_by_email(payload.email.clone(), state)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
return common_response(StatusCode::BAD_REQUEST, "User already exists");
|
||||
}
|
||||
|
||||
let hashed_password = match hash_password(&payload.password) {
|
||||
Ok(hash) => hash,
|
||||
Err(_) => {
|
||||
return common_response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Failed to hash password",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let new_user = AuthRegisterRequestDto {
|
||||
email: payload.email,
|
||||
password: hashed_password,
|
||||
fullname: payload.fullname,
|
||||
};
|
||||
|
||||
match query_create_user(new_user, state).await {
|
||||
Ok(_) => common_response(StatusCode::CREATED, "Registration successful"),
|
||||
Err(err) => {
|
||||
common_response(StatusCode::INTERNAL_SERVER_ERROR, &err.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,14 @@ pub mod auth_controller;
|
||||
pub mod auth_dto;
|
||||
pub mod auth_middleware;
|
||||
pub mod auth_repository;
|
||||
pub mod auth_service;
|
||||
|
||||
pub use auth_dto::*;
|
||||
pub use auth_repository::*;
|
||||
pub use auth_service::*;
|
||||
|
||||
pub fn auth_router() -> Router {
|
||||
Router::new().route("/login", post(auth_controller::post_login))
|
||||
Router::new()
|
||||
.route("/login", post(auth_controller::post_login))
|
||||
.route("/register", post(auth_controller::post_register))
|
||||
}
|
||||
|
||||
@@ -5,4 +5,5 @@ use utoipa::ToSchema;
|
||||
pub struct UsersItemDto {
|
||||
pub email: String,
|
||||
pub fullname: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use surrealdb::{engine::remote::ws::Client, Surreal};
|
||||
use surrealdb::{engine::remote::http::Client, Surreal};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||
|
||||
+6
-15
@@ -1,31 +1,22 @@
|
||||
use crate::{redisdb_init, Env, RedisClient};
|
||||
use crate::{surrealdb_init, SurrealClient};
|
||||
use axum::{serve, Router};
|
||||
use std::env;
|
||||
use std::{future::Future, net::SocketAddr};
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use crate::{redisdb_init, RedisClient};
|
||||
use crate::{surrealdb_init, SurrealClient};
|
||||
|
||||
pub async fn axum_init<F, Fut>(router_fn: F)
|
||||
where
|
||||
F: FnOnce(SurrealClient, RedisClient) -> Fut,
|
||||
Fut: Future<Output = Router>,
|
||||
{
|
||||
let env = Env::new();
|
||||
let surrealdb = surrealdb_init().await.expect("Failed surrealdb");
|
||||
let redisdb = redisdb_init().await.expect("Failed redisdb");
|
||||
let redisdb = redisdb_init().await.expect("Failed redisdb");
|
||||
let router = router_fn(surrealdb, redisdb).await;
|
||||
|
||||
let addr = SocketAddr::from((
|
||||
[0, 0, 0, 0],
|
||||
env::var("PORT")
|
||||
.unwrap_or_else(|_| "3000".to_string())
|
||||
.parse()
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
let port = env.port;
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||
let listener = TcpListener::bind(&addr).await.unwrap();
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
match serve(listener, router).await {
|
||||
Ok(_) => println!("Server stopped gracefully."),
|
||||
Err(err) => println!("Server encountered an error: {}", err),
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
use std::env;
|
||||
|
||||
pub struct Env {
|
||||
pub port: u16,
|
||||
pub access_token_secret: String,
|
||||
pub refresh_token_secret: String,
|
||||
pub database_url: String,
|
||||
pub database_schema: String,
|
||||
pub smtp_email: String,
|
||||
pub smtp_password: String,
|
||||
pub smtp_name: String,
|
||||
pub smpt_host: String,
|
||||
pub redis_hostname: String,
|
||||
pub fe_url: String,
|
||||
pub rust_env: String,
|
||||
pub minio_endpoint: String,
|
||||
pub minio_bucket_name: String,
|
||||
pub minio_access_key: String,
|
||||
pub minio_secret_key: String,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
port: env::var("PORT")
|
||||
.unwrap_or("3000".to_string())
|
||||
.parse()
|
||||
.unwrap_or(3000),
|
||||
access_token_secret: env::var("ACCESS_TOKEN_SECRET")
|
||||
.unwrap_or("default_access_secret".to_string()),
|
||||
refresh_token_secret: env::var("REFRESH_TOKEN_SECRET")
|
||||
.unwrap_or("default_refresh_secret".to_string()),
|
||||
database_url: env::var("DATABASE_URL")
|
||||
.unwrap_or("postgres://localhost".to_string()),
|
||||
database_schema: env::var("DATABASE_SCHEMA")
|
||||
.unwrap_or("public".to_string()),
|
||||
smtp_email: env::var("SMTP_EMAIL")
|
||||
.unwrap_or("no-reply@example.com".to_string()),
|
||||
smtp_password: env::var("SMTP_PASSWORD")
|
||||
.unwrap_or("default_smtp_password".to_string()),
|
||||
smtp_name: env::var("SMTP_NAME").unwrap_or("MyApp SMTP".to_string()),
|
||||
smpt_host: env::var("SMPT_HOST").unwrap_or("smpt.gmail.com".to_string()),
|
||||
redis_hostname: env::var("REDIS_HOSTNAME")
|
||||
.unwrap_or("localhost".to_string()),
|
||||
fe_url: env::var("FE_URL").unwrap_or("http://localhost".to_string()),
|
||||
rust_env: env::var("RUST_ENV").unwrap_or("development".to_string()),
|
||||
minio_endpoint: env::var("MINIO_ENDPOINT")
|
||||
.unwrap_or("http://localhost:9000".to_string()),
|
||||
minio_bucket_name: env::var("MINIO_BUCKET_NAME")
|
||||
.unwrap_or("default_bucket".to_string()),
|
||||
minio_access_key: env::var("MINIO_ACCESS_KEY")
|
||||
.unwrap_or("minio_access".to_string()),
|
||||
minio_secret_key: env::var("MINIO_SECRET_KEY")
|
||||
.unwrap_or("minio_secret".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,32 @@
|
||||
use super::Env;
|
||||
use axum::http::StatusCode;
|
||||
use chrono::{Duration, TimeDelta, Utc};
|
||||
use jsonwebtoken::{
|
||||
decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::env;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TokenSub {
|
||||
pub email: String,
|
||||
pub role_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Claims {
|
||||
pub exp: usize,
|
||||
pub iat: usize,
|
||||
pub email: String,
|
||||
pub sub: TokenSub,
|
||||
}
|
||||
|
||||
pub fn encode_access_token(email: &str) -> Result<String, StatusCode> {
|
||||
let secret: String = env::var("ACCESS_TOKEN_SECRET")
|
||||
.unwrap_or("this-is-secret".to_string())
|
||||
.to_string();
|
||||
pub fn encode_access_token(sub: TokenSub) -> Result<String, StatusCode> {
|
||||
let env = Env::new();
|
||||
let secret: String = env.access_token_secret;
|
||||
let now = Utc::now();
|
||||
let expire: TimeDelta = Duration::minutes(15);
|
||||
let exp: usize = (now + expire).timestamp() as usize;
|
||||
let iat: usize = now.timestamp() as usize;
|
||||
let claim = Claims {
|
||||
iat,
|
||||
exp,
|
||||
email: email.to_string(),
|
||||
};
|
||||
let claim = Claims { iat, exp, sub };
|
||||
encode(
|
||||
&Header::default(),
|
||||
&claim,
|
||||
@@ -37,9 +38,8 @@ pub fn encode_access_token(email: &str) -> Result<String, StatusCode> {
|
||||
pub fn decode_access_token(
|
||||
jwt_token: &str,
|
||||
) -> Result<TokenData<Claims>, StatusCode> {
|
||||
let secret = env::var("ACCESS_TOKEN_SECRET")
|
||||
.unwrap_or("this-is-secret".to_string())
|
||||
.to_string();
|
||||
let env = Env::new();
|
||||
let secret: String = env.access_token_secret;
|
||||
let result: Result<TokenData<Claims>, StatusCode> = decode(
|
||||
&jwt_token,
|
||||
&DecodingKey::from_secret(secret.as_ref()),
|
||||
@@ -49,19 +49,14 @@ pub fn decode_access_token(
|
||||
result
|
||||
}
|
||||
|
||||
pub fn encode_refresh_token(email: &str) -> Result<String, StatusCode> {
|
||||
let secret: String = env::var("REFRESH_TOKEN_SECRET")
|
||||
.unwrap_or("this-is-secret".to_string())
|
||||
.to_string();
|
||||
pub fn encode_refresh_token(sub: TokenSub) -> Result<String, StatusCode> {
|
||||
let env = Env::new();
|
||||
let secret: String = env.refresh_token_secret;
|
||||
let now = Utc::now();
|
||||
let expire: TimeDelta = Duration::days(1);
|
||||
let exp: usize = (now + expire).timestamp() as usize;
|
||||
let iat: usize = now.timestamp() as usize;
|
||||
let claim = Claims {
|
||||
iat,
|
||||
exp,
|
||||
email: email.to_string(),
|
||||
};
|
||||
let claim = Claims { iat, exp, sub };
|
||||
encode(
|
||||
&Header::default(),
|
||||
&claim,
|
||||
@@ -73,9 +68,8 @@ pub fn encode_refresh_token(email: &str) -> Result<String, StatusCode> {
|
||||
pub fn decode_refresh_token(
|
||||
jwt_token: &str,
|
||||
) -> Result<TokenData<Claims>, StatusCode> {
|
||||
let secret = env::var("REFRESH_TOKEN_SECRET")
|
||||
.unwrap_or("this-is-secret".to_string())
|
||||
.to_string();
|
||||
let env = Env::new();
|
||||
let secret: String = env.refresh_token_secret;
|
||||
let result: Result<TokenData<Claims>, StatusCode> = decode(
|
||||
&jwt_token,
|
||||
&DecodingKey::from_secret(secret.as_ref()),
|
||||
|
||||
+9
-10
@@ -1,18 +1,20 @@
|
||||
use super::Env;
|
||||
use lettre::message::Mailbox;
|
||||
use lettre::transport::smtp::authentication::Credentials;
|
||||
use lettre::{Message, SmtpTransport, Transport};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
|
||||
pub fn send_email(
|
||||
to: &str,
|
||||
subject: &str,
|
||||
body: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let sender_email = env::var("SMTP_EMAIL")?.to_string();
|
||||
let sender_name = env::var("SMTP_NAME")?.to_string();
|
||||
let sender_password = env::var("SMTP_PASSWORD")?.to_string();
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let env = Env::new();
|
||||
let host = env.smpt_host;
|
||||
let sender_email = env.smtp_email;
|
||||
let sender_name = env.smtp_name;
|
||||
let sender_password = env.smtp_password;
|
||||
let recipient_email = to;
|
||||
|
||||
let email = Message::builder()
|
||||
.from(Mailbox::new(
|
||||
Some(sender_name.replace("-", " ")),
|
||||
@@ -21,14 +23,11 @@ pub fn send_email(
|
||||
.to(recipient_email.parse()?)
|
||||
.subject(subject)
|
||||
.body(body.to_string())?;
|
||||
|
||||
let smtp_credentials =
|
||||
Credentials::new(sender_email, sender_password.replace("-", " "));
|
||||
|
||||
let mailer = SmtpTransport::relay("smtp.gmail.com")?
|
||||
let mailer = SmtpTransport::relay(&host)?
|
||||
.credentials(smtp_credentials)
|
||||
.build();
|
||||
|
||||
match mailer.send(&email) {
|
||||
Ok(_) => {
|
||||
println!("Email sent successfully to {}", to);
|
||||
|
||||
+4
-2
@@ -1,13 +1,15 @@
|
||||
pub mod argon;
|
||||
pub mod axum;
|
||||
pub mod enviroment;
|
||||
pub mod jsonwebtoken;
|
||||
pub mod lettre;
|
||||
pub mod redis;
|
||||
pub mod redisdb;
|
||||
pub mod surrealdb;
|
||||
|
||||
pub use argon::*;
|
||||
pub use axum::*;
|
||||
pub use enviroment::*;
|
||||
pub use jsonwebtoken::*;
|
||||
pub use lettre::*;
|
||||
pub use redis::*;
|
||||
pub use redisdb::*;
|
||||
pub use surrealdb::*;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
use redis::Client;
|
||||
use redis::RedisResult;
|
||||
use std::env;
|
||||
|
||||
pub async fn redisdb_init() -> RedisResult<Client> {
|
||||
let host_name =
|
||||
env::var("REDIS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string());
|
||||
let uri_scheme = if env::var("IS_TLS").is_ok() {
|
||||
"rediss"
|
||||
} else {
|
||||
"redis"
|
||||
};
|
||||
let url = format!("{}://{}", uri_scheme, host_name);
|
||||
let client = Client::open(url)?;
|
||||
Ok(client)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
use super::Env;
|
||||
use redis::Client;
|
||||
use redis::RedisResult;
|
||||
|
||||
pub async fn redisdb_init() -> RedisResult<Client> {
|
||||
let env = Env::new();
|
||||
let host_name = env.redis_hostname;
|
||||
let url = format!("redis://{}", host_name);
|
||||
let client = Client::open(url)?;
|
||||
Ok(client)
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
use surrealdb::engine::remote::ws::{Client, Ws};
|
||||
use surrealdb::{Result, Surreal};
|
||||
|
||||
use crate::SurrealClient;
|
||||
use surrealdb::engine::remote::http::{Client, Http};
|
||||
use surrealdb::{Result, Surreal};
|
||||
|
||||
pub async fn surrealdb_init() -> Result<SurrealClient> {
|
||||
let db = Surreal::<Client>::init();
|
||||
db.connect::<Ws>("ws://localhost:8000").await?;
|
||||
db.connect::<Http>("localhost:8000").await?;
|
||||
db.signin(surrealdb::opt::auth::Root {
|
||||
username: "root",
|
||||
password: "root",
|
||||
})
|
||||
.await?;
|
||||
db.use_ns("test").use_db("test").await?;
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user