feat(message): add comprehensive message system with database migrations and health checks
- Add database migrations for message base table with indexes for efficient querying - Implement rich content support with attachment, embed, and poll tables - Create social features including reactions, bookmarks, mentions, and read states - Add thread management with participant tracking and resolution capabilities - Include article posts with title, cover image, tags, and engagement metrics - Support scheduled messages, stickers, forwards, and interactive components - Fix UUID defaults and ensure proper uniqueness constraints for drafts - Add gRPC health server for imks and health check client for appks connectivity - Replace non-connectable 0.0.0.0 addresses with localhost in service discovery - Normalize addresses during RPC configuration to handle bind address issues
This commit is contained in:
@@ -58,12 +58,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Discover appks from etcd (priority > env).
|
||||
// etcd-registered addresses are bare "host:port" — prepend http:// for gRPC.
|
||||
// Bind address 0.0.0.0 is not connectable; replace with localhost.
|
||||
let appks_addr = etcd
|
||||
.discover_service("appks")
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|addrs| addrs.into_iter().next())
|
||||
.map(|addr| format!("http://{}", addr))
|
||||
.map(|addr| {
|
||||
let normalized = addr.replace("0.0.0.0", "127.0.0.1");
|
||||
format!("http://{}", normalized)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
std::env::var("APPKS_GRPC_ADDR")
|
||||
.unwrap_or_else(|_| "http://localhost:50051".to_string())
|
||||
@@ -183,52 +187,77 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let namespaces = namespaces.clone();
|
||||
let current_addr = current_addr.clone();
|
||||
let mut rpc = rpc_config.clone();
|
||||
// etcd-registered address is bare "host:port" — prepend scheme for gRPC
|
||||
rpc.appks_addr = if addr.starts_with("http") {
|
||||
addr
|
||||
// etcd-registered address is bare "host:port" — prepend scheme for gRPC.
|
||||
// Bind address 0.0.0.0 is not connectable; replace with localhost.
|
||||
let normalized = addr.replace("0.0.0.0", "127.0.0.1");
|
||||
rpc.appks_addr = if normalized.starts_with("http") {
|
||||
normalized
|
||||
} else {
|
||||
format!("http://{}", addr)
|
||||
format!("http://{}", normalized)
|
||||
};
|
||||
async move {
|
||||
match AppksClients::connect(&rpc).await {
|
||||
Ok(clients) => {
|
||||
match MessageService::new((*repo).clone(), clients, namespaces.clone())
|
||||
.await
|
||||
{
|
||||
Ok(svc) => {
|
||||
let svc = Arc::new(svc);
|
||||
let mut guard = service.write().await;
|
||||
*guard = Some(svc);
|
||||
// Retry with backoff — appks may have registered in etcd
|
||||
// before its gRPC server finished binding.
|
||||
let max_retries = 5;
|
||||
let mut delay = std::time::Duration::from_millis(500);
|
||||
|
||||
// Update the active appks address for health checker
|
||||
let mut addr_guard = current_addr.write().await;
|
||||
*addr_guard = Some(rpc.appks_addr.clone());
|
||||
for attempt in 1..=max_retries {
|
||||
match AppksClients::connect(&rpc).await {
|
||||
Ok(clients) => {
|
||||
match MessageService::new((*repo).clone(), clients, namespaces.clone())
|
||||
.await
|
||||
{
|
||||
Ok(svc) => {
|
||||
let svc = Arc::new(svc);
|
||||
let mut guard = service.write().await;
|
||||
*guard = Some(svc);
|
||||
|
||||
tracing::info!(
|
||||
addr = %rpc.appks_addr,
|
||||
"Message service initialized"
|
||||
);
|
||||
true
|
||||
// Update the active appks address for health checker
|
||||
let mut addr_guard = current_addr.write().await;
|
||||
*addr_guard = Some(rpc.appks_addr.clone());
|
||||
|
||||
tracing::info!(
|
||||
addr = %rpc.appks_addr,
|
||||
attempt,
|
||||
"Message service initialized"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
addr = %rpc.appks_addr,
|
||||
attempt,
|
||||
error = %e,
|
||||
"Failed to init message service"
|
||||
);
|
||||
if attempt < max_retries {
|
||||
tokio::time::sleep(delay).await;
|
||||
delay *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
addr = %rpc.appks_addr,
|
||||
error = %e,
|
||||
"Failed to init message service"
|
||||
);
|
||||
false
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
addr = %rpc.appks_addr,
|
||||
attempt,
|
||||
error = %e,
|
||||
"gRPC connect failed"
|
||||
);
|
||||
if attempt < max_retries {
|
||||
tokio::time::sleep(delay).await;
|
||||
delay *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
addr = %rpc.appks_addr,
|
||||
error = %e,
|
||||
"gRPC connect failed"
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
tracing::error!(
|
||||
addr = %rpc.appks_addr,
|
||||
max_retries,
|
||||
"All connection attempts to appks exhausted"
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user