feat: support server port reuse

This commit is contained in:
2025-05-31 01:59:04 +08:00
parent a676061b3e
commit ac7d1efb8d
32 changed files with 1111 additions and 139 deletions

View File

@@ -40,13 +40,13 @@ pub struct AppContext {
cache: CacheService,
mikan: MikanClient,
auth: AuthService,
graphql: GraphQLService,
storage: StorageService,
crypto: CryptoService,
working_dir: String,
environment: Environment,
message: MessageService,
task: OnceCell<TaskService>,
graphql: OnceCell<GraphQLService>,
}
impl AppContext {
@@ -65,7 +65,6 @@ impl AppContext {
let auth = AuthService::from_conf(config.auth).await?;
let mikan = MikanClient::from_config(config.mikan).await?;
let crypto = CryptoService::from_config(config.crypto).await?;
let graphql = GraphQLService::from_config_and_database(config.graphql, db.clone()).await?;
let ctx = Arc::new(AppContext {
config: config_cloned,
@@ -77,10 +76,10 @@ impl AppContext {
storage,
mikan,
working_dir: working_dir.to_string(),
graphql,
crypto,
message,
task: OnceCell::new(),
graphql: OnceCell::new(),
});
ctx.task
@@ -89,6 +88,12 @@ impl AppContext {
})
.await?;
ctx.graphql
.get_or_try_init(async || {
GraphQLService::from_config_and_ctx(config.graphql, ctx.clone()).await
})
.await?;
Ok(ctx)
}
}
@@ -119,7 +124,7 @@ impl AppContextTrait for AppContext {
&self.auth
}
fn graphql(&self) -> &GraphQLService {
&self.graphql
self.graphql.get().expect("graphql should be set")
}
fn storage(&self) -> &dyn StorageServiceTrait {
&self.storage

View File

@@ -1,7 +1,8 @@
use std::{net::SocketAddr, sync::Arc};
use axum::Router;
use tokio::signal;
use tokio::{net::TcpSocket, signal};
use tracing::instrument;
use super::{builder::AppBuilder, context::AppContextTrait};
use crate::{
@@ -22,14 +23,31 @@ impl App {
AppBuilder::default()
}
#[instrument(err, skip(self))]
pub async fn serve(&self) -> RecorderResult<()> {
let context = &self.context;
let config = context.config();
let listener = tokio::net::TcpListener::bind(&format!(
"{}:{}",
config.server.binding, config.server.port
))
.await?;
let listener = {
let addr: SocketAddr =
format!("{}:{}", config.server.binding, config.server.port).parse()?;
let socket = if addr.is_ipv4() {
TcpSocket::new_v4()
} else {
TcpSocket::new_v6()
}?;
socket.set_reuseaddr(true)?;
#[cfg(all(unix, not(target_os = "solaris")))]
if let Err(e) = socket.set_reuseport(true) {
tracing::warn!("Failed to set SO_REUSEPORT: {}", e);
}
socket.bind(addr)?;
socket.listen(1024)
}?;
let mut router = Router::<Arc<dyn AppContextTrait>>::new();

View File

@@ -10,10 +10,6 @@ use sea_orm_migration::MigratorTrait;
use super::DatabaseConfig;
use crate::{errors::RecorderResult, migrations::Migrator};
pub trait DatabaseServiceConnectionTrait {
fn get_database_connection(&self) -> &DatabaseConnection;
}
pub struct DatabaseService {
connection: DatabaseConnection,
#[cfg(all(any(test, feature = "playground"), feature = "testcontainers"))]

View File

@@ -25,6 +25,8 @@ pub enum RecorderError {
source: Box<fancy_regex::Error>,
},
#[snafu(transparent)]
NetAddrParseError { source: std::net::AddrParseError },
#[snafu(transparent)]
RegexError { source: regex::Error },
#[snafu(transparent)]
InvalidMethodError { source: http::method::InvalidMethod },

View File

@@ -139,7 +139,7 @@ fn add_crypto_column_output_conversion<T>(
);
}
pub fn crypto_transformer(context: &mut BuilderContext, ctx: Arc<dyn AppContextTrait>) {
pub fn add_crypto_transformers(context: &mut BuilderContext, ctx: Arc<dyn AppContextTrait>) {
add_crypto_column_input_conversion::<credential_3rd::Entity>(
context,
ctx.clone(),
@@ -150,7 +150,7 @@ pub fn crypto_transformer(context: &mut BuilderContext, ctx: Arc<dyn AppContextT
ctx.clone(),
&credential_3rd::Column::Username,
);
add_crypto_column_output_conversion::<credential_3rd::Entity>(
add_crypto_column_input_conversion::<credential_3rd::Entity>(
context,
ctx.clone(),
&credential_3rd::Column::Password,

View File

@@ -1,21 +1,27 @@
use std::sync::Arc;
use async_graphql::dynamic::*;
use once_cell::sync::OnceCell;
use sea_orm::{DatabaseConnection, EntityTrait, Iterable};
use sea_orm::{EntityTrait, Iterable};
use seaography::{Builder, BuilderContext, FilterType, FilterTypesMapHelper};
use crate::graphql::{
infra::{
filter::{
JSONB_FILTER_NAME, SUBSCRIBER_ID_FILTER_INFO, init_custom_filter_info,
register_jsonb_input_filter_to_dynamic_schema, subscriber_id_condition_function,
use crate::{
app::AppContextTrait,
graphql::{
infra::{
filter::{
JSONB_FILTER_NAME, SUBSCRIBER_ID_FILTER_INFO, init_custom_filter_info,
register_jsonb_input_filter_to_dynamic_schema, subscriber_id_condition_function,
},
guard::{guard_entity_with_subscriber_id, guard_field_with_subscriber_id},
transformer::{
add_crypto_transformers, build_filter_condition_transformer,
build_mutation_input_object_transformer,
},
util::{get_entity_column_key, get_entity_key},
},
guard::{guard_entity_with_subscriber_id, guard_field_with_subscriber_id},
transformer::{
build_filter_condition_transformer, build_mutation_input_object_transformer,
},
util::{get_entity_column_key, get_entity_key},
views::register_subscriptions_to_schema,
},
views::register_subscriptions_to_schema,
};
pub static CONTEXT: OnceCell<BuilderContext> = OnceCell::new();
@@ -88,11 +94,13 @@ where
}
pub fn build_schema(
database: DatabaseConnection,
app_ctx: Arc<dyn AppContextTrait>,
depth: Option<usize>,
complexity: Option<usize>,
) -> Result<Schema, SchemaError> {
use crate::models::*;
let database = app_ctx.db().as_ref().clone();
init_custom_filter_info();
let context = CONTEXT.get_or_init(|| {
let mut context = BuilderContext::default();
@@ -148,6 +156,7 @@ pub fn build_schema(
&mut context,
&subscriber_tasks::Column::Job,
);
add_crypto_transformers(&mut context, app_ctx);
for column in subscribers::Column::iter() {
if !matches!(column, subscribers::Column::Id) {
restrict_filter_input_for_entity::<subscribers::Entity>(
@@ -159,6 +168,7 @@ pub fn build_schema(
}
context
});
let mut builder = Builder::new(context, database.clone());
{

View File

@@ -1,8 +1,9 @@
use std::sync::Arc;
use async_graphql::dynamic::Schema;
use sea_orm::DatabaseConnection;
use super::{build_schema, config::GraphQLConfig};
use crate::errors::RecorderResult;
use crate::{app::AppContextTrait, errors::RecorderResult};
#[derive(Debug)]
pub struct GraphQLService {
@@ -10,12 +11,12 @@ pub struct GraphQLService {
}
impl GraphQLService {
pub async fn from_config_and_database(
pub async fn from_config_and_ctx(
config: GraphQLConfig,
db: DatabaseConnection,
ctx: Arc<dyn AppContextTrait>,
) -> RecorderResult<Self> {
let schema = build_schema(
db,
ctx,
config.depth_limit.and_then(|l| l.into()),
config.complexity_limit.and_then(|l| l.into()),
)?;

View File

@@ -0,0 +1,6 @@
---
source: apps/recorder/src/web/middleware/request_id.rs
assertion_line: 126
expression: id
---
"foo-barbaz"