mirror of
https://github.com/house-of-vanity/OutFleet.git
synced 2025-10-25 09:49:08 +00:00
Useradd works
This commit is contained in:
@@ -9,14 +9,17 @@ pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: Uuid,
|
||||
|
||||
/// Reference to the actual user
|
||||
pub user_id: Uuid,
|
||||
|
||||
pub server_inbound_id: Uuid,
|
||||
|
||||
pub username: String,
|
||||
|
||||
pub email: String,
|
||||
|
||||
/// Generated xray user ID (UUID for protocols like vmess/vless)
|
||||
pub xray_user_id: String,
|
||||
|
||||
/// Generated password for protocols like trojan/shadowsocks
|
||||
pub password: Option<String>,
|
||||
|
||||
pub level: i32,
|
||||
|
||||
pub is_active: bool,
|
||||
@@ -28,6 +31,12 @@ pub struct Model {
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id"
|
||||
)]
|
||||
User,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::server_inbound::Entity",
|
||||
from = "Column::ServerInboundId",
|
||||
@@ -36,6 +45,12 @@ pub enum Relation {
|
||||
ServerInbound,
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::server_inbound::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::ServerInbound.def()
|
||||
@@ -74,41 +89,46 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
/// Inbound user creation data transfer object
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CreateInboundUserDto {
|
||||
pub user_id: Uuid,
|
||||
pub server_inbound_id: Uuid,
|
||||
pub username: String,
|
||||
pub level: Option<i32>,
|
||||
}
|
||||
|
||||
impl CreateInboundUserDto {
|
||||
/// Generate email in format: username@OutFleet
|
||||
pub fn generate_email(&self) -> String {
|
||||
format!("{}@OutFleet", self.username)
|
||||
}
|
||||
|
||||
/// Generate UUID for xray user
|
||||
/// Generate UUID for xray user (for vmess/vless)
|
||||
pub fn generate_xray_user_id(&self) -> String {
|
||||
Uuid::new_v4().to_string()
|
||||
}
|
||||
|
||||
/// Generate random password (for trojan/shadowsocks)
|
||||
pub fn generate_password(&self) -> String {
|
||||
use rand::prelude::*;
|
||||
use rand::distributions::Alphanumeric;
|
||||
|
||||
thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(24)
|
||||
.map(char::from)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Inbound user update data transfer object
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpdateInboundUserDto {
|
||||
pub username: Option<String>,
|
||||
pub level: Option<i32>,
|
||||
pub is_active: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<CreateInboundUserDto> for ActiveModel {
|
||||
fn from(dto: CreateInboundUserDto) -> Self {
|
||||
let email = dto.generate_email();
|
||||
let xray_user_id = dto.generate_xray_user_id();
|
||||
|
||||
Self {
|
||||
user_id: Set(dto.user_id),
|
||||
server_inbound_id: Set(dto.server_inbound_id),
|
||||
username: Set(dto.username),
|
||||
email: Set(email),
|
||||
xray_user_id: Set(xray_user_id),
|
||||
password: Set(Some(dto.generate_password())), // Generate password for all protocols
|
||||
level: Set(dto.level.unwrap_or(0)),
|
||||
is_active: Set(true),
|
||||
..Self::new()
|
||||
@@ -121,11 +141,6 @@ impl Model {
|
||||
pub fn apply_update(self, dto: UpdateInboundUserDto) -> ActiveModel {
|
||||
let mut active_model: ActiveModel = self.into();
|
||||
|
||||
if let Some(username) = dto.username {
|
||||
let new_email = format!("{}@OutFleet", username);
|
||||
active_model.username = Set(username);
|
||||
active_model.email = Set(new_email);
|
||||
}
|
||||
if let Some(level) = dto.level {
|
||||
active_model.level = Set(level);
|
||||
}
|
||||
@@ -135,16 +150,21 @@ impl Model {
|
||||
|
||||
active_model
|
||||
}
|
||||
|
||||
/// Generate email for xray client based on user information
|
||||
pub fn generate_client_email(&self, username: &str) -> String {
|
||||
format!("{}@OutFleet", username)
|
||||
}
|
||||
}
|
||||
|
||||
/// Response model for inbound user
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct InboundUserResponse {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub server_inbound_id: Uuid,
|
||||
pub username: String,
|
||||
pub email: String,
|
||||
pub xray_user_id: String,
|
||||
pub password: Option<String>,
|
||||
pub level: i32,
|
||||
pub is_active: bool,
|
||||
pub created_at: String,
|
||||
@@ -155,10 +175,10 @@ impl From<Model> for InboundUserResponse {
|
||||
fn from(model: Model) -> Self {
|
||||
Self {
|
||||
id: model.id,
|
||||
user_id: model.user_id,
|
||||
server_inbound_id: model.server_inbound_id,
|
||||
username: model.username,
|
||||
email: model.email,
|
||||
xray_user_id: model.xray_user_id,
|
||||
password: model.password,
|
||||
level: model.level,
|
||||
is_active: model.is_active,
|
||||
created_at: model.created_at.to_rfc3339(),
|
||||
|
||||
@@ -7,6 +7,7 @@ mod m20241201_000004_create_servers_table;
|
||||
mod m20241201_000005_create_server_inbounds_table;
|
||||
mod m20241201_000006_create_user_access_table;
|
||||
mod m20241201_000007_create_inbound_users_table;
|
||||
mod m20250919_000001_update_inbound_users_schema;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -21,6 +22,7 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20241201_000005_create_server_inbounds_table::Migration),
|
||||
Box::new(m20241201_000006_create_user_access_table::Migration),
|
||||
Box::new(m20241201_000007_create_inbound_users_table::Migration),
|
||||
Box::new(m20250919_000001_update_inbound_users_schema::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -44,23 +44,23 @@ impl InboundUsersRepository {
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
/// Find user by username and inbound (for uniqueness check)
|
||||
pub async fn find_by_username_and_inbound(&self, username: &str, inbound_id: Uuid) -> Result<Option<Model>> {
|
||||
/// Find user by user_id and inbound (for uniqueness check - one user per inbound)
|
||||
pub async fn find_by_user_and_inbound(&self, user_id: Uuid, inbound_id: Uuid) -> Result<Option<Model>> {
|
||||
let user = Entity::find()
|
||||
.filter(Column::Username.eq(username))
|
||||
.filter(Column::UserId.eq(user_id))
|
||||
.filter(Column::ServerInboundId.eq(inbound_id))
|
||||
.one(&self.db)
|
||||
.await?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
/// Find user by email
|
||||
pub async fn find_by_email(&self, email: &str) -> Result<Option<Model>> {
|
||||
let user = Entity::find()
|
||||
.filter(Column::Email.eq(email))
|
||||
.one(&self.db)
|
||||
/// Find all inbound access for a specific user
|
||||
pub async fn find_by_user_id(&self, user_id: Uuid) -> Result<Vec<Model>> {
|
||||
let users = Entity::find()
|
||||
.filter(Column::UserId.eq(user_id))
|
||||
.all(&self.db)
|
||||
.await?;
|
||||
Ok(user)
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
pub async fn create(&self, dto: CreateInboundUserDto) -> Result<Model> {
|
||||
@@ -124,9 +124,9 @@ impl InboundUsersRepository {
|
||||
Ok(result.rows_affected)
|
||||
}
|
||||
|
||||
/// Check if username already exists on this inbound
|
||||
pub async fn username_exists_on_inbound(&self, username: &str, inbound_id: Uuid) -> Result<bool> {
|
||||
let exists = self.find_by_username_and_inbound(username, inbound_id).await?;
|
||||
/// Check if user already has access to this inbound
|
||||
pub async fn user_has_access_to_inbound(&self, user_id: Uuid, inbound_id: Uuid) -> Result<bool> {
|
||||
let exists = self.find_by_user_and_inbound(user_id, inbound_id).await?;
|
||||
Ok(exists.is_some())
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,11 @@ impl UserRepository {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
/// Find user by ID (alias for get_by_id)
|
||||
pub async fn find_by_id(&self, id: Uuid) -> Result<Option<Model>> {
|
||||
self.get_by_id(id).await
|
||||
}
|
||||
|
||||
/// Get user by telegram ID
|
||||
pub async fn get_by_telegram_id(&self, telegram_id: i64) -> Result<Option<Model>> {
|
||||
let user = User::find()
|
||||
|
||||
Reference in New Issue
Block a user