feat: Implement SSH Agent Connector and gRPC server

- Added `AgentConnectorTrait` and `AgentConnector` for managing agent connections.
- Introduced `SshAgentConnector` to handle SSH-related functionalities and start a gRPC server.
- Created database entities for `agents`, `certificates`, `organizations`, `public_key_revocations`, `setup_tokens`, `upstreams`, `users`, `virtual_hosts`, and `workspaces` using SeaORM.
- Developed `CertificateService` for managing certificate generation and retrieval.
- Implemented the main server logic to initialize the database connection and start the agent server.
- Configured development settings in `development.toml` for server and database connections.
This commit is contained in:
GW_MC
2026-03-21 03:09:39 +00:00
parent 9640f03d69
commit f5eb25993b
27 changed files with 1581 additions and 2 deletions

View File

@@ -0,0 +1,27 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "agents")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
pub name: String,
pub ip_address: Option<String>,
pub version: Option<String>,
pub state: String,
pub deployment_mode: Option<String>,
pub last_seen_at: Option<DateTimeWithTimeZone>,
pub capabilities: Option<Json>,
pub public_key_hash: Option<String>,
pub labels: Option<Json>,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,45 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "certificates")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
pub workspace_id: Uuid,
pub domain: String,
pub is_wildcard: bool,
pub provider: Option<String>,
pub status: Option<String>,
pub issued_at: Option<DateTimeWithTimeZone>,
pub expires_at: Option<DateTimeWithTimeZone>,
pub auto_renew: bool,
#[sea_orm(column_type = "Text", nullable)]
pub certificate_pem: Option<String>,
#[sea_orm(column_type = "Text", nullable)]
pub private_key_pem: Option<String>,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::workspaces::Entity",
from = "Column::WorkspaceId",
to = "super::workspaces::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Workspaces,
}
impl Related<super::workspaces::Entity> for Entity {
fn to() -> RelationDef {
Relation::Workspaces.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,6 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
pub mod prelude;
pub mod agents;
pub mod public_key_revocations;

View File

@@ -0,0 +1,39 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "organizations")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
pub name: String,
#[sea_orm(unique)]
pub slug: String,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
pub settings: Option<Json>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::users::Entity")]
Users,
#[sea_orm(has_many = "super::workspaces::Entity")]
Workspaces,
}
impl Related<super::users::Entity> for Entity {
fn to() -> RelationDef {
Relation::Users.def()
}
}
impl Related<super::workspaces::Entity> for Entity {
fn to() -> RelationDef {
Relation::Workspaces.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,4 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
pub use super::agents::Entity as Agents;
pub use super::public_key_revocations::Entity as PublicKeyRevocations;

View File

@@ -0,0 +1,18 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "public_key_revocations")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
pub public_key_hash: String,
pub created_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,21 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "setup_tokens")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
#[sea_orm(unique)]
pub token_hash: String,
pub expires_at: DateTimeWithTimeZone,
pub used_at: Option<DateTimeWithTimeZone>,
pub created_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,40 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "upstreams")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
pub workspace_id: Uuid,
pub name: String,
pub algorithm: String,
pub servers: Option<Json>,
pub health_check: Option<Json>,
pub keepalive_connections: Option<i32>,
pub keepalive_timeout: Option<i32>,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::workspaces::Entity",
from = "Column::WorkspaceId",
to = "super::workspaces::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Workspaces,
}
impl Related<super::workspaces::Entity> for Entity {
fn to() -> RelationDef {
Relation::Workspaces.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,39 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "users")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
#[sea_orm(unique)]
pub email: String,
pub password_hash: String,
pub name: Option<String>,
pub role: String,
pub organization_id: Option<Uuid>,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::organizations::Entity",
from = "Column::OrganizationId",
to = "super::organizations::Column::Id",
on_update = "NoAction",
on_delete = "SetNull"
)]
Organizations,
}
impl Related<super::organizations::Entity> for Entity {
fn to() -> RelationDef {
Relation::Organizations.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,44 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "virtual_hosts")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
pub workspace_id: Uuid,
pub name: String,
pub server_name: String,
pub listen_port: i32,
pub ssl_enabled: bool,
pub ssl_certificate_id: Option<Uuid>,
pub locations: Option<Json>,
pub http2_enabled: bool,
pub http3_enabled: bool,
pub gzip_enabled: bool,
pub target_agents: Option<Json>,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::workspaces::Entity",
from = "Column::WorkspaceId",
to = "super::workspaces::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Workspaces,
}
impl Related<super::workspaces::Entity> for Entity {
fn to() -> RelationDef {
Relation::Workspaces.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,70 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "workspaces")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
#[sea_orm(unique_key = "idx_workspaces_org_slug")]
pub organization_id: Uuid,
pub name: String,
#[sea_orm(unique_key = "idx_workspaces_org_slug")]
pub slug: String,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::agents::Entity")]
Agents,
#[sea_orm(has_many = "super::certificates::Entity")]
Certificates,
#[sea_orm(
belongs_to = "super::organizations::Entity",
from = "Column::OrganizationId",
to = "super::organizations::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Organizations,
#[sea_orm(has_many = "super::upstreams::Entity")]
Upstreams,
#[sea_orm(has_many = "super::virtual_hosts::Entity")]
VirtualHosts,
}
impl Related<super::agents::Entity> for Entity {
fn to() -> RelationDef {
Relation::Agents.def()
}
}
impl Related<super::certificates::Entity> for Entity {
fn to() -> RelationDef {
Relation::Certificates.def()
}
}
impl Related<super::organizations::Entity> for Entity {
fn to() -> RelationDef {
Relation::Organizations.def()
}
}
impl Related<super::upstreams::Entity> for Entity {
fn to() -> RelationDef {
Relation::Upstreams.def()
}
}
impl Related<super::virtual_hosts::Entity> for Entity {
fn to() -> RelationDef {
Relation::VirtualHosts.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,11 @@
use sea_orm::{Database, DatabaseConnection};
pub mod entities;
pub(crate) async fn establish_connection(
url: &str,
) -> Result<DatabaseConnection, Box<dyn std::error::Error + Send + Sync>> {
Database::connect(url)
.await
.map_err(|e| format!("Failed to connect to database: {}", e).into())
}