98 lines
2.9 KiB
Rust
98 lines
2.9 KiB
Rust
use crate::AppService;
|
|
use crate::error::AppError;
|
|
use chrono::Utc;
|
|
use models::WorkspaceRole;
|
|
use models::workspaces::workspace;
|
|
use models::workspaces::workspace_membership;
|
|
use sea_orm::*;
|
|
use serde::{Deserialize, Serialize};
|
|
use session::Session;
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
|
|
pub struct WorkspaceInitParams {
|
|
pub slug: String,
|
|
pub name: String,
|
|
pub description: Option<String>,
|
|
}
|
|
|
|
impl AppService {
|
|
pub async fn workspace_init(
|
|
&self,
|
|
ctx: &Session,
|
|
params: WorkspaceInitParams,
|
|
) -> Result<workspace::Model, AppError> {
|
|
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
|
let user = self.utils_find_user_by_uid(user_uid).await?;
|
|
|
|
// Validate slug format: alphanumeric, dashes, underscores
|
|
if !params
|
|
.slug
|
|
.chars()
|
|
.all(|c| c.is_alphanumeric() || c == '-' || c == '_')
|
|
{
|
|
return Err(AppError::BadRequest(
|
|
"Slug must contain only letters, numbers, hyphens and underscores".to_string(),
|
|
));
|
|
}
|
|
|
|
// Check slug uniqueness
|
|
if workspace::Entity::find()
|
|
.filter(workspace::Column::Slug.eq(¶ms.slug))
|
|
.filter(workspace::Column::DeletedAt.is_null())
|
|
.one(&self.db)
|
|
.await?
|
|
.is_some()
|
|
{
|
|
return Err(AppError::WorkspaceSlugAlreadyExists);
|
|
}
|
|
|
|
// Check name uniqueness
|
|
if workspace::Entity::find()
|
|
.filter(workspace::Column::Name.eq(¶ms.name))
|
|
.filter(workspace::Column::DeletedAt.is_null())
|
|
.one(&self.db)
|
|
.await?
|
|
.is_some()
|
|
{
|
|
return Err(AppError::WorkspaceNameAlreadyExists);
|
|
}
|
|
|
|
let txn = self.db.begin().await?;
|
|
|
|
let ws = workspace::ActiveModel {
|
|
id: Set(Uuid::now_v7()),
|
|
slug: Set(params.slug),
|
|
name: Set(params.name),
|
|
description: Set(params.description),
|
|
avatar_url: Set(None),
|
|
plan: Set("free".to_string()),
|
|
billing_email: Set(None),
|
|
stripe_customer_id: Set(None),
|
|
stripe_subscription_id: Set(None),
|
|
plan_expires_at: Set(None),
|
|
deleted_at: Set(None),
|
|
created_at: Set(Utc::now()),
|
|
updated_at: Set(Utc::now()),
|
|
};
|
|
let ws = ws.insert(&txn).await?;
|
|
|
|
let membership = workspace_membership::ActiveModel {
|
|
id: Default::default(),
|
|
workspace_id: Set(ws.id),
|
|
user_id: Set(user.uid),
|
|
role: Set(WorkspaceRole::Owner.to_string()),
|
|
status: Set("active".to_string()),
|
|
invited_by: Set(None),
|
|
joined_at: Set(Utc::now()),
|
|
invite_token: Set(None),
|
|
invite_expires_at: Set(None),
|
|
};
|
|
membership.insert(&txn).await?;
|
|
|
|
txn.commit().await?;
|
|
Ok(ws)
|
|
}
|
|
}
|
|
|
|
use uuid::Uuid;
|