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:
zhenyi
2026-06-11 23:07:38 +08:00
parent c794b818ff
commit 716f952bb6
10 changed files with 667 additions and 37 deletions
+66 -37
View File
@@ -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
}
}
};