diff --git a/Cargo.lock b/Cargo.lock index 6f366a9798560e57558cb38cd231bf450f79ddf2..c27ed1a4025b3f234c0ded9fa018c50a41005cf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1389,6 +1389,15 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +[[package]] +name = "linkify" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1986921c3c13e81df623c66a298d4b130c061bcb98a01f5b2d3ac402b1649a7f" +dependencies = [ + "memchr", +] + [[package]] name = "lock_api" version = "0.3.4" @@ -2490,6 +2499,7 @@ dependencies = [ "impl_ops", "lazy_static", "lettre", + "linkify", "log", "many-to-many", "md5", diff --git a/Cargo.toml b/Cargo.toml index cbde4f2de7cd4739d9504d38c89246f02d4600bc..8d8a3ba83f5b17203a0a37de74fd9e8c0e37094c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ ulid = "0.4.1" rand = "0.7.3" time = "0.2.16" base64 = "0.13.0" +linkify = "0.6.0" dotenv = "0.15.0" futures = "0.3.8" chrono = "0.4.15" diff --git a/set_version.sh b/set_version.sh index ffe5b037d09cdb50f4e86fa1be146f8f296a72f9..bc535409c56926cf28f56471290d7e951d7c5609 100755 --- a/set_version.sh +++ b/set_version.sh @@ -1,3 +1,3 @@ #!/bin/bash -export version=0.4.1-alpha.7-patch.2 +export version=0.4.1-alpha.8 echo "pub const VERSION: &str = \"${version}\";" > src/version.rs diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs index 1c7c96ae89015689c9489ea8a316dad62ac42ec6..7f61b70c79b2f4f0830180f4711e99a07d61c806 100644 --- a/src/database/entities/channel.rs +++ b/src/database/entities/channel.rs @@ -94,9 +94,7 @@ impl Channel { let channel_id = self.id().to_string(); ClientboundNotification::ChannelCreate(self) - .publish(channel_id) - .await - .ok(); + .publish(channel_id); Ok(()) } @@ -108,9 +106,7 @@ impl Channel { data, clear: None } - .publish(id) - .await - .ok(); + .publish(id); Ok(()) } @@ -193,9 +189,7 @@ impl Channel { })?; ClientboundNotification::ChannelDelete { id: id.to_string() } - .publish(id.to_string()) - .await - .ok(); + .publish(id.to_string()); if let Channel::Group { icon, .. } = self { if let Some(attachment) = icon { diff --git a/src/database/entities/january.rs b/src/database/entities/january.rs new file mode 100644 index 0000000000000000000000000000000000000000..881090892d4ff1e4819ffc2040d0e701ebd24cf8 --- /dev/null +++ b/src/database/entities/january.rs @@ -0,0 +1,71 @@ +use serde::{Serialize, Deserialize}; +use linkify::{LinkFinder, LinkKind}; +use crate::util::{result::{Error, Result}, variables::JANUARY_URL}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum MediaSize { + Large, + Preview, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Media { + pub url: String, + pub width: isize, + pub height: isize, + pub size: MediaSize, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Metadata { + #[serde(skip_serializing_if = "Option::is_none")] + title: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + description: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + image: Option<Media>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "type")] +pub enum Embed { + Website(Metadata), + Image(Media), + None, +} + +impl Embed { + pub async fn generate(content: String) -> Result<Vec<Embed>> { + // FIXME: allow multiple links + let mut finder = LinkFinder::new(); + finder.kinds(&[LinkKind::Url]); + let links: Vec<_> = finder.links(&content).collect(); + + if links.len() == 0 { + return Err(Error::LabelMe); + } + + let link = &links[0]; + + let client = reqwest::Client::new(); + let result = client + .get(&format!("{}/embed", *JANUARY_URL)) + .query(&[("url", link.as_str())]) + .send() + .await; + + match result { + Err(_) => return Err(Error::LabelMe), + Ok(result) => match result.status() { + reqwest::StatusCode::OK => { + let res: Embed = result.json() + .await + .map_err(|_| Error::InvalidOperation)?; + + Ok(vec![ res ]) + }, + _ => return Err(Error::LabelMe), + }, + } + } +} diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs index 4960b786fc34bc4361934842d2abc5d783e836be..5e23d4154c1d3966117c558f032f7f6637402e87 100644 --- a/src/database/entities/message.rs +++ b/src/database/entities/message.rs @@ -17,12 +17,6 @@ use web_push::{ ContentEncoding, SubscriptionInfo, VapidSignatureBuilder, WebPushClient, WebPushMessageBuilder, }; -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(tag = "type")] -pub enum MessageEmbed { - Dummy, -} - #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(tag = "type")] pub enum SystemMessage { @@ -60,7 +54,7 @@ pub struct Message { #[serde(skip_serializing_if = "Option::is_none")] pub edited: Option<DateTime>, #[serde(skip_serializing_if = "Option::is_none")] - pub embeds: Option<MessageEmbed>, + pub embeds: Option<Vec<Embed>>, } impl Message { @@ -138,11 +132,12 @@ impl Message { _ => {} } + self.process_embed(); + let enc = serde_json::to_string(&self).unwrap(); ClientboundNotification::Message(self) - .publish(channel.id().to_string()) - .await - .unwrap(); + .publish(channel.id().to_string()); + /* Web Push Test Code @@ -162,78 +157,114 @@ impl Message { _ => {} } - // Fetch their corresponding sessions. - if let Ok(mut cursor) = get_collection("accounts") - .find( - doc! { - "_id": { - "$in": target_ids + async_std::task::spawn(async move { + // Fetch their corresponding sessions. + if let Ok(mut cursor) = get_collection("accounts") + .find( + doc! { + "_id": { + "$in": target_ids + }, + "sessions.subscription": { + "$exists": true + } }, - "sessions.subscription": { - "$exists": true - } - }, - FindOptions::builder() - .projection(doc! { "sessions": 1 }) - .build(), - ) - .await - { - let mut subscriptions = vec![]; - while let Some(result) = cursor.next().await { - if let Ok(doc) = result { - if let Ok(sessions) = doc.get_array("sessions") { - for session in sessions { - if let Some(doc) = session.as_document() { - if let Ok(sub) = doc.get_document("subscription") { - let endpoint = sub.get_str("endpoint").unwrap().to_string(); - let p256dh = sub.get_str("p256dh").unwrap().to_string(); - let auth = sub.get_str("auth").unwrap().to_string(); + FindOptions::builder() + .projection(doc! { "sessions": 1 }) + .build(), + ) + .await + { + let mut subscriptions = vec![]; + while let Some(result) = cursor.next().await { + if let Ok(doc) = result { + if let Ok(sessions) = doc.get_array("sessions") { + for session in sessions { + if let Some(doc) = session.as_document() { + if let Ok(sub) = doc.get_document("subscription") { + let endpoint = sub.get_str("endpoint").unwrap().to_string(); + let p256dh = sub.get_str("p256dh").unwrap().to_string(); + let auth = sub.get_str("auth").unwrap().to_string(); - subscriptions - .push(SubscriptionInfo::new(endpoint, p256dh, auth)); + subscriptions + .push(SubscriptionInfo::new(endpoint, p256dh, auth)); + } } } } } } - } - if subscriptions.len() > 0 { - let client = WebPushClient::new(); - let key = - base64::decode_config(VAPID_PRIVATE_KEY.clone(), base64::URL_SAFE).unwrap(); + if subscriptions.len() > 0 { + let client = WebPushClient::new(); + let key = + base64::decode_config(VAPID_PRIVATE_KEY.clone(), base64::URL_SAFE).unwrap(); - for subscription in subscriptions { - let mut builder = WebPushMessageBuilder::new(&subscription).unwrap(); - let sig_builder = - VapidSignatureBuilder::from_pem(std::io::Cursor::new(&key), &subscription) - .unwrap(); - let signature = sig_builder.build().unwrap(); - builder.set_vapid_signature(signature); - builder.set_payload(ContentEncoding::AesGcm, enc.as_bytes()); - let m = builder.build().unwrap(); - client.send(m).await.ok(); + for subscription in subscriptions { + let mut builder = WebPushMessageBuilder::new(&subscription).unwrap(); + let sig_builder = + VapidSignatureBuilder::from_pem(std::io::Cursor::new(&key), &subscription) + .unwrap(); + let signature = sig_builder.build().unwrap(); + builder.set_vapid_signature(signature); + builder.set_payload(ContentEncoding::AesGcm, enc.as_bytes()); + let m = builder.build().unwrap(); + client.send(m).await.ok(); + } } } - } + }); Ok(()) } - pub async fn publish_update(&self, data: JsonValue) -> Result<()> { + pub async fn publish_update(self, data: JsonValue) -> Result<()> { let channel = self.channel.clone(); ClientboundNotification::MessageUpdate { id: self.id.clone(), data, } - .publish(channel) - .await - .ok(); + .publish(channel); + self.process_embed(); Ok(()) } + pub fn process_embed(&self) { + if let Content::Text(text) = &self.content { + let id = self.id.clone(); + let content = text.clone(); + let channel = self.channel.clone(); + async_std::task::spawn(async move { + if let Ok(embeds) = Embed::generate(content).await { + if let Ok(bson) = to_bson(&embeds) { + if let Ok(_) = get_collection("messages") + .update_one( + doc! { + "_id": &id + }, + doc! { + "$set": { + "embeds": bson + } + }, + None, + ) + .await { + ClientboundNotification::MessageUpdate { + id, + data: json!({ + "embeds": embeds + }), + } + .publish(channel); + } + } + } + }); + } + } + pub async fn delete(&self) -> Result<()> { if let Some(attachment) = &self.attachment { attachment.delete().await?; @@ -256,9 +287,7 @@ impl Message { ClientboundNotification::MessageDelete { id: self.id.clone(), } - .publish(channel) - .await - .ok(); + .publish(channel); if let Some(attachment) = &self.attachment { get_collection("attachments") diff --git a/src/database/entities/mod.rs b/src/database/entities/mod.rs index cdc9a435f953c8348d5a526a50677b7ed2e20493..90d99cba85014183b8970542c50c5597b7462d4a 100644 --- a/src/database/entities/mod.rs +++ b/src/database/entities/mod.rs @@ -1,9 +1,11 @@ +mod guild; mod autumn; +mod january; mod channel; -mod guild; mod message; mod user; +pub use january::*; pub use autumn::*; pub use channel::*; pub use guild::*; diff --git a/src/notifications/events.rs b/src/notifications/events.rs index 9ff4cf0b8f238c50d349e89a8834b93ce767d05a..9366f16f60a68582eaba3d5a8a75feaa7b292f12 100644 --- a/src/notifications/events.rs +++ b/src/notifications/events.rs @@ -107,9 +107,11 @@ pub enum ClientboundNotification { } impl ClientboundNotification { - pub async fn publish(self, topic: String) -> Result<(), String> { - prehandle_hook(&self); // ! TODO: this should be moved to pubsub - hive_pubsub::backend::mongo::publish(get_hive(), &topic, self).await + pub fn publish(self, topic: String) { + async_std::task::spawn(async move { + prehandle_hook(&self); // ! TODO: this should be moved to pubsub + hive_pubsub::backend::mongo::publish(get_hive(), &topic, self).await.ok(); + }); } } diff --git a/src/notifications/websocket.rs b/src/notifications/websocket.rs index 9df9b1b0e933b07cb2db6a1e5c4f0ea42043cd2d..cc0e06c21a92c392921b7651bf35ab323c6844ee 100644 --- a/src/notifications/websocket.rs +++ b/src/notifications/websocket.rs @@ -133,9 +133,7 @@ async fn accept(stream: TcpStream) { id: id.clone(), online: true, } - .publish(id) - .await - .ok(); + .publish(id); } } Err(_) => { @@ -173,9 +171,7 @@ async fn accept(stream: TcpStream) { id: channel.clone(), user, } - .publish(channel) - .await - .ok(); + .publish(channel); } else { send(ClientboundNotification::Error( WebSocketError::AlreadyAuthenticated, @@ -196,9 +192,7 @@ async fn accept(stream: TcpStream) { id: channel.clone(), user, } - .publish(channel) - .await - .ok(); + .publish(channel); } else { send(ClientboundNotification::Error( WebSocketError::AlreadyAuthenticated, @@ -238,9 +232,7 @@ async fn accept(stream: TcpStream) { id: id.clone(), online: false, } - .publish(id) - .await - .ok(); + .publish(id); } } diff --git a/src/routes/channels/delete_channel.rs b/src/routes/channels/delete_channel.rs index 28dd54b8fc0681e04f697db66b37180411654627..9575ae61d700d90675607770ab59b105f0e2db92 100644 --- a/src/routes/channels/delete_channel.rs +++ b/src/routes/channels/delete_channel.rs @@ -95,9 +95,7 @@ pub async fn req(user: User, target: Ref) -> Result<()> { id: id.clone(), user: user.id.clone(), } - .publish(id.clone()) - .await - .ok(); + .publish(id.clone()); Message::create( "00000000000000000000000000".to_string(), diff --git a/src/routes/channels/edit_channel.rs b/src/routes/channels/edit_channel.rs index 5242ec9e2a2c21a0435e8153e86b41d2fa3101be..d5f114ea03ee64a7273bd8d4fac17bad90a62fdd 100644 --- a/src/routes/channels/edit_channel.rs +++ b/src/routes/channels/edit_channel.rs @@ -101,9 +101,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { data: json!(set), clear: data.remove } - .publish(id.clone()) - .await - .ok(); + .publish(id.clone()); if let Some(name) = data.name { Message::create( diff --git a/src/routes/channels/group_add_member.rs b/src/routes/channels/group_add_member.rs index f0c26706bd53d843c3b3649822d18acc685a4c9f..f95090af5ead7953a9c6aee682d211c83b97848c 100644 --- a/src/routes/channels/group_add_member.rs +++ b/src/routes/channels/group_add_member.rs @@ -52,9 +52,7 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> { id: id.clone(), user: member.id.clone(), } - .publish(id.clone()) - .await - .ok(); + .publish(id.clone()); Message::create( "00000000000000000000000000".to_string(), diff --git a/src/routes/channels/group_remove_member.rs b/src/routes/channels/group_remove_member.rs index 63c0f2062fa2316498b3378163e54d18431dc7bd..f26922f85baa777c944da5c6f893fb15b1d30ec7 100644 --- a/src/routes/channels/group_remove_member.rs +++ b/src/routes/channels/group_remove_member.rs @@ -49,9 +49,7 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> { id: id.clone(), user: member.id.clone(), } - .publish(id.clone()) - .await - .ok(); + .publish(id.clone()); Message::create( "00000000000000000000000000".to_string(), diff --git a/src/routes/users/add_friend.rs b/src/routes/users/add_friend.rs index 4b1441bf322341baaa3697985a2af559c37cade3..ad0a494c27787d7375b1808c167d83b98d1c8a45 100644 --- a/src/routes/users/add_friend.rs +++ b/src/routes/users/add_friend.rs @@ -74,21 +74,19 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> { .from_override(&target_user, RelationshipStatus::Friend) .await?; - try_join!( - ClientboundNotification::UserRelationship { - id: user.id.clone(), - user: target_user, - status: RelationshipStatus::Friend - } - .publish(user.id.clone()), - ClientboundNotification::UserRelationship { - id: target_id.to_string(), - user, - status: RelationshipStatus::Friend - } - .publish(target_id.to_string()) - ) - .ok(); + ClientboundNotification::UserRelationship { + id: user.id.clone(), + user: target_user, + status: RelationshipStatus::Friend + } + .publish(user.id.clone()); + + ClientboundNotification::UserRelationship { + id: target_id.to_string(), + user, + status: RelationshipStatus::Friend + } + .publish(target_id.to_string()); Ok(json!({ "status": "Friend" })) } @@ -136,21 +134,20 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> { let user = user .from_override(&target_user, RelationshipStatus::Incoming) .await?; - try_join!( - ClientboundNotification::UserRelationship { - id: user.id.clone(), - user: target_user, - status: RelationshipStatus::Outgoing - } - .publish(user.id.clone()), - ClientboundNotification::UserRelationship { - id: target_id.to_string(), - user, - status: RelationshipStatus::Incoming - } - .publish(target_id.to_string()) - ) - .ok(); + + ClientboundNotification::UserRelationship { + id: user.id.clone(), + user: target_user, + status: RelationshipStatus::Outgoing + } + .publish(user.id.clone()); + + ClientboundNotification::UserRelationship { + id: target_id.to_string(), + user, + status: RelationshipStatus::Incoming + } + .publish(target_id.to_string()); Ok(json!({ "status": "Outgoing" })) } diff --git a/src/routes/users/block_user.rs b/src/routes/users/block_user.rs index 10f69f23dfbce8f02ea1ed5ab21e4a9b4bafdb56..6e06e0e127827a1eb9359dd41e1446253f57846d 100644 --- a/src/routes/users/block_user.rs +++ b/src/routes/users/block_user.rs @@ -38,9 +38,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { user: target, status: RelationshipStatus::Blocked, } - .publish(user.id.clone()) - .await - .ok(); + .publish(user.id.clone()); Ok(json!({ "status": "Blocked" })) } @@ -84,21 +82,19 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { .await?; let target_id = target.id.clone(); - try_join!( - ClientboundNotification::UserRelationship { - id: user.id.clone(), - user: target, - status: RelationshipStatus::Blocked - } - .publish(user.id.clone()), - ClientboundNotification::UserRelationship { - id: target_id.clone(), - user, - status: RelationshipStatus::BlockedOther - } - .publish(target_id) - ) - .ok(); + ClientboundNotification::UserRelationship { + id: user.id.clone(), + user: target, + status: RelationshipStatus::Blocked + } + .publish(user.id.clone()); + + ClientboundNotification::UserRelationship { + id: target_id.clone(), + user, + status: RelationshipStatus::BlockedOther + } + .publish(target_id); Ok(json!({ "status": "Blocked" })) } @@ -146,21 +142,19 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { .await?; let target_id = target.id.clone(); - try_join!( - ClientboundNotification::UserRelationship { - id: user.id.clone(), - user: target, - status: RelationshipStatus::Blocked - } - .publish(user.id.clone()), - ClientboundNotification::UserRelationship { - id: target_id.clone(), - user, - status: RelationshipStatus::BlockedOther - } - .publish(target_id) - ) - .ok(); + ClientboundNotification::UserRelationship { + id: user.id.clone(), + user: target, + status: RelationshipStatus::Blocked + } + .publish(user.id.clone()); + + ClientboundNotification::UserRelationship { + id: target_id.clone(), + user, + status: RelationshipStatus::BlockedOther + } + .publish(target_id); Ok(json!({ "status": "Blocked" })) } diff --git a/src/routes/users/change_username.rs b/src/routes/users/change_username.rs index 1daa11fc129e11117bf29720bb1bd2156095f96d..461818a3e49a8a9e1928b80699d6ed124417bb54 100644 --- a/src/routes/users/change_username.rs +++ b/src/routes/users/change_username.rs @@ -60,9 +60,7 @@ pub async fn req( data: json!(data.0), clear: None } - .publish(user.id.clone()) - .await - .ok(); + .publish(user.id.clone()); Ok(()) } diff --git a/src/routes/users/edit_user.rs b/src/routes/users/edit_user.rs index 5ce6b247a803662b2ab7c9845a23521e0f2be7b8..2f1197eda7bb4c0f93fe0524ac3369df2690981c 100644 --- a/src/routes/users/edit_user.rs +++ b/src/routes/users/edit_user.rs @@ -135,9 +135,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> data: json!({ "status": status }), clear: None } - .publish(user.id.clone()) - .await - .ok(); + .publish(user.id.clone()); } if let Some(avatar) = attachment { @@ -146,9 +144,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> data: json!({ "avatar": avatar }), clear: None } - .publish(user.id.clone()) - .await - .ok(); + .publish(user.id.clone()); } if let Some(clear) = data.remove { @@ -157,9 +153,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> data: json!({}), clear: Some(clear) } - .publish(user.id.clone()) - .await - .ok(); + .publish(user.id.clone()); } if remove_avatar { diff --git a/src/routes/users/remove_friend.rs b/src/routes/users/remove_friend.rs index ec0176a1f1c04c3e7f1bc1d1539480e31c2c4a6f..5426cf74f4250d37d7a51ae81a85f1882923760d 100644 --- a/src/routes/users/remove_friend.rs +++ b/src/routes/users/remove_friend.rs @@ -53,21 +53,19 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { .await?; let target_id = target.id.clone(); - try_join!( - ClientboundNotification::UserRelationship { - id: user.id.clone(), - user: target, - status: RelationshipStatus::None - } - .publish(user.id.clone()), - ClientboundNotification::UserRelationship { - id: target_id.clone(), - user, - status: RelationshipStatus::None - } - .publish(target_id) - ) - .ok(); + ClientboundNotification::UserRelationship { + id: user.id.clone(), + user: target, + status: RelationshipStatus::None + } + .publish(user.id.clone()); + + ClientboundNotification::UserRelationship { + id: target_id.clone(), + user, + status: RelationshipStatus::None + } + .publish(target_id); Ok(json!({ "status": "None" })) } diff --git a/src/routes/users/unblock_user.rs b/src/routes/users/unblock_user.rs index 48db2e7d3b8732f3e79d56641460aee6db257c40..4e2e59c0e5b61220b451cedc5994da7f64dd26e1 100644 --- a/src/routes/users/unblock_user.rs +++ b/src/routes/users/unblock_user.rs @@ -40,9 +40,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { user: target, status: RelationshipStatus::BlockedOther, } - .publish(user.id.clone()) - .await - .ok(); + .publish(user.id.clone()); Ok(json!({ "status": "BlockedOther" })) } @@ -84,21 +82,19 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { .await?; let target_id = target.id.clone(); - try_join!( - ClientboundNotification::UserRelationship { - id: user.id.clone(), - user: target, - status: RelationshipStatus::None - } - .publish(user.id.clone()), - ClientboundNotification::UserRelationship { - id: target_id.clone(), - user: user, - status: RelationshipStatus::None - } - .publish(target_id) - ) - .ok(); + ClientboundNotification::UserRelationship { + id: user.id.clone(), + user: target, + status: RelationshipStatus::None + } + .publish(user.id.clone()); + + ClientboundNotification::UserRelationship { + id: target_id.clone(), + user: user, + status: RelationshipStatus::None + } + .publish(target_id); Ok(json!({ "status": "None" })) } diff --git a/src/util/variables.rs b/src/util/variables.rs index 241d2aba132b7db90b0188b59f6b542b4fbab46d..1bd71e9461cd403507e293b66256468dea8ea617 100644 --- a/src/util/variables.rs +++ b/src/util/variables.rs @@ -18,6 +18,8 @@ lazy_static! { pub static ref AUTUMN_URL: String = env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string()); + pub static ref JANUARY_URL: String = + env::var("JANUARY_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string()); pub static ref VOSO_URL: String = env::var("VOSO_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string()); pub static ref VOSO_WS_HOST: String =