fix(all): Organizing APIs and docs.

This commit is contained in:
Rodrigo Rodriguez 2025-02-21 12:17:52 -03:00
parent 3798976340
commit eadbed756f
11 changed files with 361 additions and 220 deletions

8
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

23
.idea/general-bots.iml generated Normal file
View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/gb-api/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-auth/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-auth/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/gb-automation/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-core/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-image/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-media/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-messaging/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-migrations/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-monitoring/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-storage/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-testing/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gb-testing/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/general-bots.iml" filepath="$PROJECT_DIR$/.idea/general-bots.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -211,6 +211,82 @@ Licensed under terms specified in workspace configuration.
- Global expansion
- Enterprise features
# Infrastructure Compliance Checklist - ISO 27001, HIPAA, LGPD
| ✓ | Requirement | Component | Standard | Implementation Steps |
|---|-------------|-----------|-----------|---------------------|
| ⬜ | TLS 1.3 Configuration | Nginx | All | Configure modern SSL parameters and ciphers in `/etc/nginx/conf.d/ssl.conf` |
| ⬜ | Access Logging | Nginx | All | Enable detailed access logs with privacy fields in `/etc/nginx/nginx.conf` |
| ⬜ | Rate Limiting | Nginx | ISO 27001 | Implement rate limiting rules in location blocks |
| ⬜ | WAF Rules | Nginx | HIPAA | Install and configure ModSecurity with OWASP rules |
| ⬜ | Reverse Proxy Security | Nginx | All | Configure security headers (X-Frame-Options, HSTS, CSP) |
| ⬜ | MFA Implementation | Zitadel | All | Enable and enforce MFA for all administrative accounts |
| ⬜ | RBAC Configuration | Zitadel | All | Set up role-based access control with least privilege |
| ⬜ | Password Policy | Zitadel | All | Configure strong password requirements (length, complexity, history) |
| ⬜ | OAuth2/OIDC Setup | Zitadel | ISO 27001 | Configure secure OAuth flows and token policies |
| ⬜ | Audit Logging | Zitadel | All | Enable comprehensive audit logging for user activities |
| ⬜ | Encryption at Rest | Garage (S3) | All | Configure encrypted storage with key management |
| ⬜ | Bucket Policies | Garage (S3) | All | Implement strict bucket access policies |
| ⬜ | Object Versioning | Garage (S3) | HIPAA | Enable versioning for data recovery capability |
| ⬜ | Access Logging | Garage (S3) | All | Enable detailed access logging for object operations |
| ⬜ | Lifecycle Rules | Garage (S3) | LGPD | Configure data retention and deletion policies |
| ⬜ | DKIM/SPF/DMARC | Stalwart | All | Configure email authentication mechanisms |
| ⬜ | Mail Encryption | Stalwart | All | Enable TLS for mail transport |
| ⬜ | Content Filtering | Stalwart | All | Implement content scanning and filtering rules |
| ⬜ | Mail Archiving | Stalwart | HIPAA | Configure compliant email archiving |
| ⬜ | Sieve Filtering | Stalwart | All | Implement security-focused mail filtering rules |
| ⬜ | System Hardening | Ubuntu | All | Apply CIS Ubuntu Linux benchmarks |
| ⬜ | System Updates | Ubuntu | All | Configure unattended-upgrades for security patches |
| ⬜ | Audit Daemon | Ubuntu | All | Configure auditd for system event logging |
| ⬜ | Firewall Rules | Ubuntu | All | Configure UFW with restrictive rules |
| ⬜ | Disk Encryption | Ubuntu | All | Implement LUKS encryption for system disks |
| ⬜ | SELinux/AppArmor | Ubuntu | All | Enable and configure mandatory access control |
| ⬜ | Monitoring Setup | All | All | Install and configure Prometheus + Grafana |
| ⬜ | Log Aggregation | All | All | Implement centralized logging (e.g., ELK Stack) |
| ⬜ | Backup System | All | All | Configure automated backup system with encryption |
| ⬜ | Network Isolation | All | All | Implement proper network segmentation |
## Documentation Requirements
1. **Security Policies**
- Information Security Policy
- Access Control Policy
- Password Policy
- Data Protection Policy
- Incident Response Plan
2. **Procedures**
- Backup and Recovery Procedures
- Change Management Procedures
- Access Review Procedures
- Security Incident Procedures
- Data Breach Response Procedures
3. **Technical Documentation**
- Network Architecture Diagrams
- System Configuration Documentation
- Security Controls Documentation
- Encryption Standards Documentation
- Logging and Monitoring Documentation
4. **Compliance Records**
- Risk Assessment Reports
- Audit Logs
- Training Records
- Incident Reports
- Access Review Records
## Regular Maintenance Tasks
- Weekly security updates
- Monthly access reviews
- Quarterly compliance audits
- Annual penetration testing
- Bi-annual disaster recovery testing
---
Built with ❤️ from Brazil, using Rust for maximum performance and reliability.

View file

@ -42,8 +42,8 @@ mod tests {
kind: "test".to_string(),
content: "integration test".to_string(),
metadata: serde_json::Value::Object(serde_json::Map::new()),
created_at: chrono::Utc::now(),
shard_key: 0,
created_at: Some(chrono::Utc::now()),
shard_key: Some(0),
};
let response = app

View file

@ -24,16 +24,239 @@ pub fn create_router(message_processor: MessageProcessor) -> Router {
let state = Arc::new(ApiState {
message_processor: Mutex::new(message_processor),
});
Router::new()
.route("/health", get(|| async { "OK" }))
.route("/messages", post(send_message))
.route("/messages/:id", get(get_message))
.route("/rooms", post(create_room))
.route("/rooms/:id", get(get_room))
.route("/rooms/:id/join", post(join_room))
.route("/ws", get(websocket_handler))
.with_state(state)
Router::new()
// File & Document Management
.route("/files/upload", post(upload_file))
.route("/files/download", post(download))
.route("/files/copy", post(copy_file))
.route("/files/move", post(move_file))
.route("/files/delete", post(delete_file))
.route("/files/getContents", post(get_file_contents))
.route("/files/save", post(save_file))
.route("/files/createFolder", post(create_folder))
.route("/files/shareFolder", post(share_folder))
.route("/files/dirFolder", post(dir_folder))
.route("/files/list", post(get_files))
.route("/files/search", post(search_files))
.route("/files/recent", post(get_recent_files))
.route("/files/favorite", post(toggle_favorite))
.route("/files/versions", post(get_file_versions))
.route("/files/restore", post(restore_file_version))
.route("/files/permissions", post(set_file_permissions))
.route("/files/quota", get(get_storage_quota))
.route("/files/shared", get(get_shared_files))
.route("/files/sync/status", get(get_sync_status))
.route("/files/sync/start", post(start_sync))
.route("/files/sync/stop", post(stop_sync))
// Document Processing
.route("/docs/merge", post(merge_documents))
.route("/docs/convert", post(convert_document))
.route("/docs/fill", post(fill_document))
.route("/docs/export", post(export_document))
.route("/docs/import", post(import_document))
// Groups & Organizations
.route("/groups/create", post(create_group))
.route("/groups/update", put(update_group))
.route("/groups/delete", delete(delete_group))
.route("/groups/list", get(get_groups))
.route("/groups/search", post(search_groups))
.route("/groups/members", get(get_group_members))
.route("/groups/members/add", post(add_group_member))
.route("/groups/members/remove", post(remove_group_member))
.route("/groups/permissions", post(set_group_permissions))
.route("/groups/settings", post(update_group_settings))
.route("/groups/analytics", get(get_group_analytics))
.route("/groups/join/request", post(request_group_join))
.route("/groups/join/approve", post(approve_join_request))
.route("/groups/join/reject", post(reject_join_request))
.route("/groups/invites/send", post(send_group_invite))
.route("/groups/invites/list", get(list_group_invites))
// Teams & Projects
.route("/teams/create", post(create_team))
.route("/teams/update", put(update_team))
.route("/teams/delete", delete(delete_team))
.route("/teams/list", get(get_teams))
.route("/teams/search", post(search_teams))
.route("/teams/members", get(get_team_members))
.route("/teams/members/add", post(add_team_member))
.route("/teams/members/remove", post(remove_team_member))
.route("/teams/roles", post(set_team_roles))
.route("/teams/permissions", post(set_team_permissions))
.route("/teams/settings", post(update_team_settings))
.route("/teams/analytics", get(get_team_analytics))
.route("/teams/projects/create", post(create_project))
.route("/teams/projects/list", get(get_projects))
.route("/teams/projects/update", put(update_project))
.route("/teams/projects/delete", delete(delete_project))
.route("/teams/reports/generate", post(generate_team_report))
.route("/teams/activity", get(get_team_activity))
// Conversations & Real-time Communication
.route("/conversations/create", post(create_conversation))
.route("/conversations/join", post(join_conversation))
.route("/conversations/leave", post(leave_conversation))
.route("/conversations/members", get(get_conversation_members))
.route("/conversations/messages", get(get_messages))
.route("/conversations/messages/send", post(send_message))
.route("/conversations/messages/edit", put(edit_message))
.route("/conversations/messages/delete", delete(delete_message))
.route("/conversations/messages/react", post(react_to_message))
.route("/conversations/messages/pin", post(pin_message))
.route("/conversations/messages/search", post(search_messages))
.route("/conversations/calls/start", post(start_call))
.route("/conversations/calls/join", post(join_call))
.route("/conversations/calls/leave", post(leave_call))
.route("/conversations/calls/mute", post(mute_participant))
.route("/conversations/calls/unmute", post(unmute_participant))
.route("/conversations/screen/share", post(share_screen))
.route("/conversations/screen/stop", post(stop_screen_share))
.route("/conversations/recording/start", post(start_recording))
.route("/conversations/recording/stop", post(stop_recording))
.route("/conversations/whiteboard/create", post(create_whiteboard))
.route("/conversations/whiteboard/collaborate", post(collaborate_whiteboard))
// Communication Services
.route("/comm/email/send", post(send_email))
.route("/comm/email/template", post(send_template_email))
.route("/comm/email/schedule", post(schedule_email))
.route("/comm/email/cancel", post(cancel_scheduled_email))
.route("/comm/sms/send", post(send_sms))
.route("/comm/sms/bulk", post(send_bulk_sms))
.route("/comm/notifications/send", post(send_notification))
.route("/comm/notifications/preferences", post(set_notification_preferences))
.route("/comm/broadcast/send", post(send_broadcast))
.route("/comm/contacts/import", post(import_contacts))
.route("/comm/contacts/export", post(export_contacts))
.route("/comm/contacts/sync", post(sync_contacts))
.route("/comm/contacts/groups", post(manage_contact_groups))
// User Management & Authentication
.route("/users/create", post(create_user))
.route("/users/update", put(update_user))
.route("/users/delete", delete(delete_user))
.route("/users/list", get(get_users))
.route("/users/search", post(search_users))
.route("/users/profile", get(get_user_profile))
.route("/users/profile/update", put(update_profile))
.route("/users/settings", post(update_user_settings))
.route("/users/permissions", post(set_user_permissions))
.route("/users/roles", post(manage_user_roles))
.route("/users/status", post(update_user_status))
.route("/users/presence", get(get_user_presence))
.route("/users/activity", get(get_user_activity))
.route("/users/security/2fa/enable", post(enable_2fa))
.route("/users/security/2fa/disable", post(disable_2fa))
.route("/users/security/devices", get(get_registered_devices))
.route("/users/security/sessions", get(get_active_sessions))
.route("/users/notifications/settings", post(update_notification_settings))
// Calendar & Task Management
.route("/calendar/events/create", post(create_event))
.route("/calendar/events/update", put(update_event))
.route("/calendar/events/delete", delete(delete_event))
.route("/calendar/events/list", get(get_calendar_events))
.route("/calendar/events/search", post(search_events))
.route("/calendar/availability/check", post(check_availability))
.route("/calendar/schedule/meeting", post(schedule_meeting))
.route("/calendar/reminders/set", post(set_reminder))
.route("/tasks/create", post(create_task))
.route("/tasks/update", put(update_task))
.route("/tasks/delete", delete(delete_task))
.route("/tasks/list", get(get_tasks))
.route("/tasks/assign", post(assign_task))
.route("/tasks/status/update", put(update_task_status))
.route("/tasks/priority/set", post(set_task_priority))
.route("/tasks/dependencies/set", post(set_task_dependencies))
// Storage & Data Management
.route("/storage/save", post(save_to_storage))
.route("/storage/batch", post(save_batch_to_storage))
.route("/storage/json", post(save_json_to_storage))
.route("/storage/delete", delete(delete_from_storage))
.route("/storage/quota/check", get(check_storage_quota))
.route("/storage/cleanup", post(cleanup_storage))
.route("/storage/backup/create", post(create_backup))
.route("/storage/backup/restore", post(restore_backup))
.route("/storage/archive", post(archive_data))
.route("/storage/metrics", get(get_storage_metrics))
// Automation & Workflows
.route("/automation/workflow/create", post(create_workflow))
.route("/automation/workflow/update", put(update_workflow))
.route("/automation/workflow/delete", delete(delete_workflow))
.route("/automation/workflow/execute", post(execute_workflow))
.route("/automation/workflow/status", get(get_workflow_status))
.route("/automation/triggers/create", post(create_trigger))
.route("/automation/triggers/list", get(list_triggers))
.route("/automation/schedule/create", post(create_schedule))
.route("/automation/schedule/update", put(update_schedule))
.route("/automation/actions/create", post(create_action))
.route("/automation/actions/execute", post(execute_action))
.route("/automation/rules/create", post(create_rule))
.route("/automation/rules/evaluate", post(evaluate_rules))
// Analytics & Reporting
.route("/analytics/dashboard", get(get_dashboard_data))
.route("/analytics/reports/generate", post(generate_report))
.route("/analytics/reports/schedule", post(schedule_report))
.route("/analytics/metrics/collect", post(collect_metrics))
.route("/analytics/insights/generate", post(generate_insights))
.route("/analytics/trends/analyze", post(analyze_trends))
.route("/analytics/export", post(export_analytics))
// System & Administration
.route("/admin/system/status", get(get_system_status))
.route("/admin/system/metrics", get(get_system_metrics))
.route("/admin/logs/view", get(view_logs))
.route("/admin/logs/export", post(export_logs))
.route("/admin/config/update", post(update_config))
.route("/admin/maintenance/schedule", post(schedule_maintenance))
.route("/admin/backup/create", post(create_system_backup))
.route("/admin/backup/restore", post(restore_system_backup))
.route("/admin/users/manage", post(manage_system_users))
.route("/admin/roles/manage", post(manage_system_roles))
.route("/admin/quotas/manage", post(manage_quotas))
.route("/admin/licenses/manage", post(manage_licenses))
// Integration & External Services
.route("/integrations/list", get(list_integrations))
.route("/integrations/install", post(install_integration))
.route("/integrations/configure", post(configure_integration))
.route("/integrations/uninstall", post(uninstall_integration))
.route("/integrations/status", get(get_integration_status))
.route("/integrations/sync", post(sync_integration_data))
.route("/integrations/webhook/create", post(create_webhook))
.route("/integrations/webhook/manage", post(manage_webhooks))
// AI & Machine Learning
.route("/ai/analyze/text", post(analyze_text))
.route("/ai/analyze/image", post(analyze_image))
.route("/ai/generate/text", post(generate_text))
.route("/ai/generate/image", post(generate_image))
.route("/ai/translate", post(translate_content))
.route("/ai/summarize", post(summarize_content))
.route("/ai/recommend", post(get_recommendations))
.route("/ai/train/model", post(train_custom_model))
.route("/ai/predict", post(make_prediction))
// Security & Compliance
.route("/security/audit/logs", get(get_audit_logs))
.route("/security/compliance/check", post(check_compliance))
.route("/security/threats/scan", post(scan_for_threats))
.route("/security/access/review", post(review_access))
.route("/security/encryption/manage", post(manage_encryption))
.route("/security/certificates/manage", post(manage_certificates))
// Health & Monitoring
.route("/health", get(health_check))
.route("/health/detailed", get(detailed_health_check))
.route("/monitoring/status", get(get_monitoring_status))
.route("/monitoring/alerts", get(get_active_alerts))
.route("/monitoring/metrics", get(get_monitoring_metrics))
.with_state(state)
}
async fn handle_ws_connection(

View file

@ -1,12 +1,10 @@
mod kafka;
mod rabbitmq;
mod redis_pubsub;
mod websocket;
mod processor;
pub mod models;
pub use kafka::Kafka;
pub use rabbitmq::RabbitMQ;
pub use redis_pubsub::RedisPubSub;
pub use websocket::WebSocketClient;
pub use processor::MessageProcessor;
@ -38,10 +36,6 @@ mod tests {
let redis_client = Client::open("redis://localhost")
.expect("Failed to create Redis client");
let redis = RedisPubSub::new(Arc::new(redis_client));
let rabbitmq = RabbitMQ::new("amqp://localhost:5672")
.await
.unwrap();
let mut websocket = WebSocketClient::connect("ws://localhost:8080")
.await
.unwrap();
@ -59,10 +53,6 @@ mod tests {
.await
.unwrap();
rabbitmq.publish("", "test.key", &test_message)
.await
.unwrap();
websocket.send_message(&serde_json::to_string(&test_message).unwrap())
.await
.unwrap();
@ -83,8 +73,8 @@ mod tests {
kind: "test".to_string(),
content: "test content".to_string(),
metadata: serde_json::Value::Object(serde_json::Map::new()),
created_at: chrono::Utc::now(),
shard_key: 0,
created_at: Some(chrono::Utc::now()),
shard_key: Some(0),
};
let envelope = MessageEnvelope {

View file

@ -7,7 +7,7 @@ use tracing::instrument;
use crate::MessageEnvelope;
use tokio::sync::broadcast; // Add this import
use std::sync::Arc;
use tracing::{error, info}; // Add error and info macros here
use tracing::{error}; // Add error and info macros here
pub struct MessageProcessor {
@ -109,8 +109,8 @@ mod tests {
kind: "test".to_string(),
content: "test content".to_string(),
metadata: serde_json::Value::Object(serde_json::Map::new()),
created_at: chrono::Utc::now(),
shard_key: 0,
created_at: Some(chrono::Utc::now()),
shard_key: Some(0),
}
}

View file

@ -1,193 +0,0 @@
use gb_core::{Result, Error};
use lapin::{
options::*,
types::FieldTable,
Connection, ConnectionProperties,
Channel,
BasicProperties,
};
use serde::{de::DeserializeOwned, Serialize};
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing::{instrument, error};
use futures::StreamExt;
pub struct RabbitMQ {
connection: Arc<Connection>,
channel: Arc<Mutex<Channel>>,
}
impl Clone for RabbitMQ {
fn clone(&self) -> Self {
Self {
connection: self.connection.clone(),
channel: self.channel.clone(),
}
}
}
impl RabbitMQ {
pub async fn new(url: &str) -> Result<Self> {
let connection = Connection::connect(
url,
ConnectionProperties::default(),
)
.await
.map_err(|e| Error::internal(format!("RabbitMQ connection error: {}", e)))?;
let channel = connection.create_channel()
.await
.map_err(|e| Error::internal(format!("RabbitMQ channel error: {}", e)))?;
Ok(Self {
connection: Arc::new(connection),
channel: Arc::new(Mutex::new(channel)),
})
}
#[instrument(skip(self, message))]
pub async fn publish<T: Serialize>(
&self,
exchange: &str,
routing_key: &str,
message: &T,
) -> Result<()> {
let payload = serde_json::to_string(message)
.map_err(|e| Error::internal(format!("Serialization error: {}", e)))?;
let channel = self.channel.lock().await;
channel.basic_publish(
exchange,
routing_key,
BasicPublishOptions::default(),
payload.as_bytes(),
BasicProperties::default(),
)
.await
.map_err(|e| Error::internal(format!("RabbitMQ publish error: {}", e)))?;
Ok(())
}
#[instrument(skip(self, handler))]
pub async fn subscribe<T, F, Fut>(
&self,
queue: &str,
handler: F,
) -> Result<()>
where
T: DeserializeOwned,
F: Fn(T) -> Fut,
Fut: std::future::Future<Output = Result<()>>,
{
let channel = self.channel.lock().await;
channel.queue_declare(
queue,
QueueDeclareOptions::default(),
FieldTable::default(),
)
.await
.map_err(|e| Error::internal(format!("RabbitMQ queue declare error: {}", e)))?;
let mut consumer = channel.basic_consume(
queue,
"consumer",
BasicConsumeOptions::default(),
FieldTable::default(),
)
.await
.map_err(|e| Error::internal(format!("RabbitMQ consume error: {}", e)))?;
while let Some(delivery) = consumer.next().await {
match delivery {
Ok(delivery) => {
if let Ok(payload) = String::from_utf8(delivery.data.clone()) {
match serde_json::from_str::<T>(&payload) {
Ok(value) => {
if let Err(e) = handler(value).await {
error!("Handler error: {}", e);
}
}
Err(e) => error!("Deserialization error: {}", e),
}
}
delivery.ack(BasicAckOptions::default())
.await
.map_err(|e| error!("Ack error: {}", e)).ok();
}
Err(e) => error!("Consumer error: {}", e),
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::*;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use std::time::Duration;
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
struct TestMessage {
id: Uuid,
content: String,
}
#[fixture]
async fn rabbitmq() -> RabbitMQ {
RabbitMQ::new("amqp://localhost:5672")
.await
.unwrap()
}
#[fixture]
fn test_message() -> TestMessage {
TestMessage {
id: Uuid::new_v4(),
content: "test message".to_string(),
}
}
#[rstest]
#[tokio::test]
async fn test_publish_subscribe(
#[future] rabbitmq: RabbitMQ,
test_message: TestMessage,
) {
let queue = "test_queue";
let routing_key = "test_routing_key";
let rabbitmq = rabbitmq.await;
let rabbitmq_clone = rabbitmq.clone();
let test_message_clone = test_message.clone();
let handle = tokio::spawn(async move {
let test_message_ref = test_message_clone.clone();
let handler = move |msg: TestMessage| {
let expected_msg = test_message_ref.clone();
async move {
assert_eq!(msg, expected_msg);
Ok(())
}
};
rabbitmq_clone.subscribe(queue, handler).await.unwrap();
});
tokio::time::sleep(Duration::from_millis(100)).await;
rabbitmq.publish("", routing_key, &test_message)
.await
.unwrap();
tokio::time::sleep(Duration::from_secs(1)).await;
handle.abort();
}
}

0
lib.rs Normal file
View file