email http functions
This commit is contained in:
parent
b9f4a67e0c
commit
df1ccdb8cd
@ -6,7 +6,9 @@
|
|||||||
* error log
|
* error log
|
||||||
* account lookup w/ pw reset
|
* account lookup w/ pw reset
|
||||||
|
|
||||||
* nice to have
|
* treats
|
||||||
|
* constructs jiggle when clicked
|
||||||
|
* background colour changes depending on time of day
|
||||||
|
|
||||||
* bot game grind
|
* bot game grind
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,17 @@ const addState = connect(
|
|||||||
.catch(error => errorToast(error));
|
.catch(error => errorToast(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setEmail(email) {
|
||||||
|
postData('/account/email', { email })
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.success) return errorToast(data.error_message);
|
||||||
|
infoToast('Email set. Please confirm your address.');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(error => errorToast(error));
|
||||||
|
}
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
postData('/account/logout').then(() => window.location.reload(true));
|
postData('/account/logout').then(() => window.location.reload(true));
|
||||||
}
|
}
|
||||||
@ -42,6 +53,7 @@ const addState = connect(
|
|||||||
ping,
|
ping,
|
||||||
logout,
|
logout,
|
||||||
setPassword,
|
setPassword,
|
||||||
|
setEmail,
|
||||||
sendConstructSpawn,
|
sendConstructSpawn,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -54,6 +66,7 @@ class AccountStatus extends Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
setPassword: { current: '', password: '', confirm: ''},
|
setPassword: { current: '', password: '', confirm: ''},
|
||||||
|
email: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +76,7 @@ class AccountStatus extends Component {
|
|||||||
ping,
|
ping,
|
||||||
logout,
|
logout,
|
||||||
setPassword,
|
setPassword,
|
||||||
|
setEmail,
|
||||||
sendConstructSpawn,
|
sendConstructSpawn,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
@ -84,13 +98,13 @@ class AccountStatus extends Component {
|
|||||||
<dt>Subscription</dt>
|
<dt>Subscription</dt>
|
||||||
<dd>{account.subscribed ? 'some date' : 'unsubscribed'}</dd>
|
<dd>{account.subscribed ? 'some date' : 'unsubscribed'}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<button><a href={`mailto:support@mnml.gg?subject=Account%20Support:%20${account.name}`}>✉ support</a></button>
|
<button><a href={`mailto:humans@mnml.gg?subject=Account%20Support:%20${account.name}`}>✉ support</a></button>
|
||||||
<button onClick={() => logout()}>Logout</button>
|
<button onClick={() => logout()}>Logout</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="email">Email:</label>
|
<label for="email">Email Settings:</label>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Current Email</dt>
|
<dt>Recovery Email</dt>
|
||||||
<dd>{account.email ? account.email : 'No email set'}</dd>
|
<dd>{account.email ? account.email : 'No email set'}</dd>
|
||||||
<dt>Status</dt>
|
<dt>Status</dt>
|
||||||
<dd>{account.email_confirmed ? 'Confirmed' : 'Unconfirmed'}</dd>
|
<dd>{account.email_confirmed ? 'Confirmed' : 'Unconfirmed'}</dd>
|
||||||
@ -99,9 +113,11 @@ class AccountStatus extends Component {
|
|||||||
class="login-input"
|
class="login-input"
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
placeholder="new email"
|
value={this.state.email}
|
||||||
|
onInput={linkState(this, 'email')}
|
||||||
|
placeholder="recovery@email.tld"
|
||||||
/>
|
/>
|
||||||
<button>Update</button>
|
<button onClick={() => setEmail(this.state.email)}>Update</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="current">Password:</label>
|
<label for="current">Password:</label>
|
||||||
|
|||||||
@ -6,7 +6,9 @@ exports.up = async knex => {
|
|||||||
table.string('name', 42).notNullable().unique();
|
table.string('name', 42).notNullable().unique();
|
||||||
table.string('password').notNullable();
|
table.string('password').notNullable();
|
||||||
|
|
||||||
table.string('token', 64).notNullable();
|
table.string('token', 64)
|
||||||
|
.notNullable()
|
||||||
|
.index();
|
||||||
table.timestamp('token_expiry').notNullable();
|
table.timestamp('token_expiry').notNullable();
|
||||||
|
|
||||||
table.bigInteger('balance')
|
table.bigInteger('balance')
|
||||||
@ -18,7 +20,6 @@ exports.up = async knex => {
|
|||||||
.notNullable();
|
.notNullable();
|
||||||
|
|
||||||
table.index('name');
|
table.index('name');
|
||||||
table.index('id');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await knex.schema.raw(`
|
await knex.schema.raw(`
|
||||||
|
|||||||
35
ops/migrations/20190825172701_email.js
Normal file
35
ops/migrations/20190825172701_email.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
exports.up = async knex => {
|
||||||
|
await knex.schema.createTable('email', table => {
|
||||||
|
table.timestamps(true, true);
|
||||||
|
|
||||||
|
table.uuid('id')
|
||||||
|
.primary();
|
||||||
|
.index();
|
||||||
|
|
||||||
|
table.uuid('account')
|
||||||
|
.notNullable()
|
||||||
|
.index();
|
||||||
|
|
||||||
|
table.foreign('account')
|
||||||
|
.references('id')
|
||||||
|
.inTable('accounts')
|
||||||
|
.onDelete('CASCADE');
|
||||||
|
|
||||||
|
table.string('email', 128)
|
||||||
|
.unique()
|
||||||
|
.notNullable()
|
||||||
|
.index();
|
||||||
|
|
||||||
|
table.string('confirm_token', 64)
|
||||||
|
.notNullable()
|
||||||
|
.index();
|
||||||
|
|
||||||
|
table.bool('confirmed')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async () => {};
|
||||||
@ -27,6 +27,7 @@ fern = { version = "0.5", features = ["colored"] }
|
|||||||
|
|
||||||
iron = "0.6"
|
iron = "0.6"
|
||||||
bodyparser = "0.8"
|
bodyparser = "0.8"
|
||||||
|
urlencoded = "0.6"
|
||||||
persistent = "0.4"
|
persistent = "0.4"
|
||||||
router = "0.6"
|
router = "0.6"
|
||||||
mount = "0.4"
|
mount = "0.4"
|
||||||
@ -34,4 +35,7 @@ cookie = "0.12"
|
|||||||
crossbeam-channel = "0.3"
|
crossbeam-channel = "0.3"
|
||||||
ws = "0.8"
|
ws = "0.8"
|
||||||
|
|
||||||
|
lettre = "0.9"
|
||||||
|
lettre_email = "0.9"
|
||||||
|
|
||||||
stripe-rust = { version = "0.10.4", features = ["webhooks"] }
|
stripe-rust = { version = "0.10.4", features = ["webhooks"] }
|
||||||
|
|||||||
@ -15,6 +15,7 @@ use instance;
|
|||||||
use pg::{Db, PgPool};
|
use pg::{Db, PgPool};
|
||||||
use rpc::RpcMessage;
|
use rpc::RpcMessage;
|
||||||
use warden::{GameEvent};
|
use warden::{GameEvent};
|
||||||
|
use mail::Mail;
|
||||||
|
|
||||||
pub type EventsTx = Sender<Event>;
|
pub type EventsTx = Sender<Event>;
|
||||||
type Id = usize;
|
type Id = usize;
|
||||||
@ -34,6 +35,7 @@ pub struct Events {
|
|||||||
pub tx: Sender<Event>,
|
pub tx: Sender<Event>,
|
||||||
rx: Receiver<Event>,
|
rx: Receiver<Event>,
|
||||||
|
|
||||||
|
mail: Sender<Mail>,
|
||||||
warden: Sender<GameEvent>,
|
warden: Sender<GameEvent>,
|
||||||
queue: Option<PvpRequest>,
|
queue: Option<PvpRequest>,
|
||||||
|
|
||||||
@ -62,11 +64,12 @@ struct WsClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Events {
|
impl Events {
|
||||||
pub fn new(tx: Sender<Event>, rx: Receiver<Event>, warden: Sender<GameEvent>) -> Events {
|
pub fn new(tx: Sender<Event>, rx: Receiver<Event>, warden: Sender<GameEvent>, mail: Sender<Mail>) -> Events {
|
||||||
Events {
|
Events {
|
||||||
tx,
|
tx,
|
||||||
rx,
|
rx,
|
||||||
warden,
|
warden,
|
||||||
|
mail,
|
||||||
queue: None,
|
queue: None,
|
||||||
clients: HashMap::new(),
|
clients: HashMap::new(),
|
||||||
}
|
}
|
||||||
@ -150,7 +153,7 @@ impl Events {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Event::Push(id, msg) => {
|
Event::Push(id, msg) => {
|
||||||
info!("push id={:?} msg={:?}", id, msg);
|
info!("push id={:?}", id);
|
||||||
|
|
||||||
let mut subs = 0;
|
let mut subs = 0;
|
||||||
let mut dead = vec![];
|
let mut dead = vec![];
|
||||||
|
|||||||
@ -6,7 +6,10 @@ use iron::prelude::*;
|
|||||||
use iron::status;
|
use iron::status;
|
||||||
use iron::typemap::Key;
|
use iron::typemap::Key;
|
||||||
use iron::mime::Mime;
|
use iron::mime::Mime;
|
||||||
|
use iron::modifiers::Redirect;
|
||||||
|
use iron::Url;
|
||||||
use iron::{typemap, BeforeMiddleware,AfterMiddleware};
|
use iron::{typemap, BeforeMiddleware,AfterMiddleware};
|
||||||
|
use urlencoded::UrlEncodedQuery;
|
||||||
use persistent::Read;
|
use persistent::Read;
|
||||||
use router::Router;
|
use router::Router;
|
||||||
use mount::{Mount};
|
use mount::{Mount};
|
||||||
@ -14,6 +17,7 @@ use serde::{Serialize, Deserialize};
|
|||||||
|
|
||||||
use acp;
|
use acp;
|
||||||
use account;
|
use account;
|
||||||
|
use mail;
|
||||||
use pg::PgPool;
|
use pg::PgPool;
|
||||||
use payments::{stripe};
|
use payments::{stripe};
|
||||||
|
|
||||||
@ -290,6 +294,68 @@ fn set_password(req: &mut Request) -> IronResult<Response> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Deserialize)]
|
||||||
|
struct SetEmail {
|
||||||
|
email: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_email(req: &mut Request) -> IronResult<Response> {
|
||||||
|
let state = req.get::<Read<State>>().unwrap();
|
||||||
|
let params = match req.get::<bodyparser::Struct<SetEmail>>() {
|
||||||
|
Ok(Some(b)) => b,
|
||||||
|
_ => return Err(IronError::from(MnmlHttpError::BadRequest)),
|
||||||
|
};
|
||||||
|
|
||||||
|
match req.extensions.get::<account::Account>() {
|
||||||
|
Some(a) => {
|
||||||
|
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
|
||||||
|
let id = match mail::insert(&mut tx, a.id, ¶ms.email) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("{:?}", e);
|
||||||
|
return Err(MnmlHttpError::ServerError.into());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("email added id={:?} account={:?} email={:?}", id, a, params.email);
|
||||||
|
|
||||||
|
tx.commit().or(Err(MnmlHttpError::ServerError))?;
|
||||||
|
|
||||||
|
Ok(json_response(status::Ok, Json::Message("email set. confirmation required".to_string())))
|
||||||
|
},
|
||||||
|
None => Err(IronError::from(MnmlHttpError::Unauthorized)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm(req: &mut Request) -> IronResult<Response> {
|
||||||
|
let state = req.get::<Read<State>>().unwrap();
|
||||||
|
|
||||||
|
match req.get_ref::<UrlEncodedQuery>() {
|
||||||
|
Ok(ref hashmap) => {
|
||||||
|
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
|
||||||
|
let token = match hashmap.get("confirm_token") {
|
||||||
|
Some(t) => &t[0],
|
||||||
|
None => return Err(IronError::from(MnmlHttpError::BadRequest)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let confirmation = match mail::confirm_email(&mut tx, token.to_string()) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return Err(IronError::from(MnmlHttpError::NotFound))
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("email confirmed email={:?} account={:?}", confirmation.0, confirmation.1);
|
||||||
|
|
||||||
|
tx.commit().or(Err(MnmlHttpError::ServerError))?;
|
||||||
|
Ok(Response::with((status::Found, Redirect(Url::parse("https://mnml.gg").unwrap()))))
|
||||||
|
},
|
||||||
|
Err(_) => Err(IronError::from(MnmlHttpError::BadRequest)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const MAX_BODY_LENGTH: usize = 1024 * 1024 * 10;
|
const MAX_BODY_LENGTH: usize = 1024 * 1024 * 10;
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
@ -306,7 +372,8 @@ fn account_mount() -> Router {
|
|||||||
router.post("logout", logout, "logout");
|
router.post("logout", logout, "logout");
|
||||||
router.post("register", register, "register");
|
router.post("register", register, "register");
|
||||||
router.post("password", set_password, "set_password");
|
router.post("password", set_password, "set_password");
|
||||||
router.post("email", logout, "email");
|
router.post("email", set_email, "set_email");
|
||||||
|
router.post("email/confirm/", confirm, "confirm");
|
||||||
|
|
||||||
router
|
router
|
||||||
}
|
}
|
||||||
|
|||||||
148
server/src/mail.rs
Normal file
148
server/src/mail.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use rand::distributions::Alphanumeric;
|
||||||
|
use std::iter;
|
||||||
|
use postgres::transaction::Transaction;
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
use failure::{err_msg, format_err};
|
||||||
|
|
||||||
|
use crossbeam_channel::Receiver;
|
||||||
|
use lettre::smtp::authentication::{Credentials, Mechanism};
|
||||||
|
use lettre::smtp::ConnectionReuseParameters;
|
||||||
|
use lettre::smtp::error::Error as MailError;
|
||||||
|
use lettre::smtp::extension::ClientId;
|
||||||
|
use lettre::smtp::response::Response;
|
||||||
|
use lettre::{SendableEmail, SmtpClient, SmtpTransport, Transport};
|
||||||
|
use lettre_email::Email;
|
||||||
|
|
||||||
|
pub enum Mail {
|
||||||
|
Recover { email: String, name: String, token: String },
|
||||||
|
Confirm { email: String, name: String, token: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
// create link that will set a token
|
||||||
|
// put msg saying pls reset your password
|
||||||
|
// redirect to main page cause cbf
|
||||||
|
|
||||||
|
fn recover(email: String, name: String, token: String) -> SendableEmail {
|
||||||
|
Email::builder()
|
||||||
|
.from("machines@mnml.gg")
|
||||||
|
.to(email)
|
||||||
|
.subject("recovery phase")
|
||||||
|
.text(format!("{:},\nyour token has been reset to\n{:}\n", name, token))
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm(email: String, name: String, token: String) -> SendableEmail {
|
||||||
|
let confirm_body = format!("{:},
|
||||||
|
please click the link below to confirm your email
|
||||||
|
http://mnml.gg/api/account/email/confirm/{:}
|
||||||
|
", name, token);
|
||||||
|
|
||||||
|
Email::builder()
|
||||||
|
.from("machines@mnml.gg")
|
||||||
|
.to(email)
|
||||||
|
.subject("recovery phase")
|
||||||
|
.text(confirm_body)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_mail(mailer: &mut SmtpTransport, mail: Mail) -> Result<Response, MailError> {
|
||||||
|
let msg = match mail {
|
||||||
|
Mail::Recover { email, name, token } => recover(email, name, token),
|
||||||
|
Mail::Confirm { email, name, token } => confirm(email, name, token),
|
||||||
|
};
|
||||||
|
|
||||||
|
mailer.send(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn confirm_email(tx: &mut Transaction, token: String) -> Result<(String, Uuid), Error> {
|
||||||
|
let query = "
|
||||||
|
UPDATE emails
|
||||||
|
SET confirmed = true, updated_at = now()
|
||||||
|
WHERE token = $1
|
||||||
|
RETURNING id, email, account
|
||||||
|
";
|
||||||
|
|
||||||
|
let result = tx
|
||||||
|
.query(query, &[&token])?;
|
||||||
|
|
||||||
|
let row = result.iter().next()
|
||||||
|
.ok_or(format_err!("token not found {:?}", token))?;
|
||||||
|
|
||||||
|
let _id: Uuid = row.get(0);
|
||||||
|
let email: String = row.get(1);
|
||||||
|
let account: Uuid = row.get(2);
|
||||||
|
|
||||||
|
return Ok((email, account));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(tx: &mut Transaction, account: Uuid, email: &String) -> Result<Uuid, Error> {
|
||||||
|
let id = Uuid::new_v4();
|
||||||
|
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
let token: String = iter::repeat(())
|
||||||
|
.map(|()| rng.sample(Alphanumeric))
|
||||||
|
.take(64)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let query = "
|
||||||
|
INSERT INTO emails (id, account, email, token)
|
||||||
|
VALUES ($1, $2, $3, $4);
|
||||||
|
";
|
||||||
|
|
||||||
|
let result = tx
|
||||||
|
.query(query, &[&id, &account, &email, &token])?;
|
||||||
|
|
||||||
|
let row = match result.iter().next() {
|
||||||
|
Some(row) => row,
|
||||||
|
None => return Err(err_msg("no email inserted")),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listen(rx: Receiver<Mail>) {
|
||||||
|
let sender = env::var("MAIL_ADDRESS")
|
||||||
|
.expect("MAIL_ADDRESS must be set");
|
||||||
|
|
||||||
|
let password = env::var("MAIL_PASSWORD")
|
||||||
|
.expect("MAIL_PASSWORD must be set");
|
||||||
|
|
||||||
|
let domain = env::var("MAIL_DOMAIN")
|
||||||
|
.expect("MAIL_DOMAIN must be set");
|
||||||
|
|
||||||
|
let mut mailer = SmtpClient::new_simple("smtp.gmail.com").unwrap()
|
||||||
|
.hello_name(ClientId::Domain(domain))
|
||||||
|
.credentials(Credentials::new(sender, password))
|
||||||
|
.smtp_utf8(true)
|
||||||
|
.authentication_mechanism(Mechanism::Plain)
|
||||||
|
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited)
|
||||||
|
.transport();
|
||||||
|
|
||||||
|
info!("mail connected");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match rx.recv() {
|
||||||
|
Ok(m) => match send_mail(&mut mailer, m) {
|
||||||
|
Ok(r) => info!("{:?}", r),
|
||||||
|
Err(e) => warn!("{:?}", e),
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
panic!("mail thread cannot continue");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicitly close the SMTP transaction as we enabled connection reuse
|
||||||
|
// mailer.close();
|
||||||
|
}
|
||||||
|
|
||||||
@ -21,11 +21,15 @@ extern crate stripe;
|
|||||||
|
|
||||||
extern crate iron;
|
extern crate iron;
|
||||||
extern crate bodyparser;
|
extern crate bodyparser;
|
||||||
|
extern crate urlencoded;
|
||||||
extern crate persistent;
|
extern crate persistent;
|
||||||
extern crate router;
|
extern crate router;
|
||||||
extern crate mount;
|
extern crate mount;
|
||||||
extern crate cookie;
|
extern crate cookie;
|
||||||
|
|
||||||
|
extern crate lettre;
|
||||||
|
extern crate lettre_email;
|
||||||
|
|
||||||
extern crate ws;
|
extern crate ws;
|
||||||
extern crate crossbeam_channel;
|
extern crate crossbeam_channel;
|
||||||
|
|
||||||
@ -37,6 +41,7 @@ mod game;
|
|||||||
mod instance;
|
mod instance;
|
||||||
mod item;
|
mod item;
|
||||||
mod img;
|
mod img;
|
||||||
|
mod mail;
|
||||||
mod mob;
|
mod mob;
|
||||||
mod mtx;
|
mod mtx;
|
||||||
mod names;
|
mod names;
|
||||||
@ -116,8 +121,8 @@ fn setup_logger() -> Result<(), fern::InitError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dotenv::from_path(Path::new("/etc/mnml/server.conf")).ok();
|
|
||||||
setup_logger().unwrap();
|
setup_logger().unwrap();
|
||||||
|
dotenv::from_path(Path::new("/etc/mnml/gs.conf")).ok();
|
||||||
|
|
||||||
let pool = pg::create_pool();
|
let pool = pg::create_pool();
|
||||||
let http_pool = pool.clone();
|
let http_pool = pool.clone();
|
||||||
@ -130,9 +135,11 @@ fn main() {
|
|||||||
let events_warden_tx = warden_tx.clone();
|
let events_warden_tx = warden_tx.clone();
|
||||||
let warden_tick_tx = warden_tx.clone();
|
let warden_tick_tx = warden_tx.clone();
|
||||||
|
|
||||||
|
let (mail_tx, mail_rx) = unbounded();
|
||||||
|
|
||||||
// create a clone of the tx so ws handler can tell events
|
// create a clone of the tx so ws handler can tell events
|
||||||
// about connection status
|
// about connection status
|
||||||
let events = events::Events::new(events_tx, events_rx, events_warden_tx);
|
let events = events::Events::new(events_tx, events_rx, events_warden_tx, mail_tx);
|
||||||
let warden = warden::Warden::new(warden_tx, warden_rx, events.tx.clone(), pool.clone());
|
let warden = warden::Warden::new(warden_tx, warden_rx, events.tx.clone(), pool.clone());
|
||||||
|
|
||||||
let pg_pool = pool.clone();
|
let pg_pool = pool.clone();
|
||||||
@ -142,6 +149,7 @@ fn main() {
|
|||||||
spawn(move || warden::upkeep_tick(warden_tick_tx));
|
spawn(move || warden::upkeep_tick(warden_tick_tx));
|
||||||
spawn(move || pg::listen(pg_pool, pg_events_tx));
|
spawn(move || pg::listen(pg_pool, pg_events_tx));
|
||||||
spawn(move || events.listen());
|
spawn(move || events.listen());
|
||||||
|
spawn(move || mail::listen(mail_rx));
|
||||||
|
|
||||||
// the main thread becomes this ws listener
|
// the main thread becomes this ws listener
|
||||||
let rpc_pool = pool.clone();
|
let rpc_pool = pool.clone();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user