rsync -r --delete --exclude=\*~ library specs $(DEPLOY_BASE)/.
rsync -r $(FILEASSETS) $(addprefix $(WASM_PACKED)/, $(WASM_ASSETS)) \
$(DEPLOY_BASE)/assets/
+ rsync -r nwtemplates/*.tera $(DEPLOY_BASE)/nwtemplates/
ssh -o BatchMode=true $(DEPLOY_USER) $(DEPLOY_FINISH)
git branch -f $(DEPLOYED_BRANCH)
save_dir = "/home/rustcargo/Rustup/Game/server"
command_socket = "/home/rustcargo/Rustup/Game/server/command.socket"
template_dir = "/home/ian/Rustup/Game/server/templates"
+nwtemplate_dir = "/home/ian/Rustup/Game/server/nwtemplates"
bundled_sources = "/home/rustcargo/Rustup/Game/server/target/bundled-sources"
wasm_dir = "/home/rustcargo/Rustup/Game/server/target/packed-wasm"
let mut access = ap.refer(&mut rma.access);
+ access.metavar("EMAIL-ADDRESS").add_option(
+ &["--email"],
+ MapStore(|addr| Ok(Some(
+ TokenByEmail { addr: addr.to_string() }.into()
+ ))),
+ "send token by email, to EMAIL-ADDRESS (RFC822 recipient field syntax)"
+ );
access.add_option(&["--url-on-stdout"],
StoreConst(Some(UrlOnStdout.into())),
"show game access url by printing to stdout");
pub sse_wildcard_url: Option<String>,
pub rocket_workers: Option<u16>,
pub template_dir: Option<String>,
+ pub nwtemplate_dir: Option<String>,
pub wasm_dir: Option<String>,
pub log: Option<toml::Value>,
pub bundled_sources: Option<String>,
pub sse_wildcard_url: Option<(String, String)>,
pub rocket_workers: u16,
pub template_dir: String,
+ pub nwtemplate_dir: String,
pub wasm_dir: String,
pub log: LogSpecification,
pub bundled_sources: String,
let ServerConfigSpec {
base_dir, save_dir, command_socket, debug,
http_port, public_url, sse_wildcard_url, rocket_workers,
- template_dir, wasm_dir,
- log, bundled_sources, shapelibs,
+ template_dir, nwtemplate_dir, wasm_dir,
+ log, bundled_sources, shapelibs, sendmail,
} = spec;
let defpath = |specd: Option<String>, leaf: &str| -> String {
let command_socket = defpath(command_socket, "var/command.socket");
let template_dir = defpath(template_dir, "assets" );
let wasm_dir = defpath(wasm_dir, "assets" );
+ let nwtemplate_dir = defpath(nwtemplate_dir, "nwtemplates" );
let bundled_sources = defpath(bundled_sources, "bundled-sources" );
const DEFAULT_LIBRARY_GLOB : &str = "library/*.toml";
vec![ shapelib::Config1::PathGlob(glob) ]
});
+ let sendmail = sendmail.unwrap_or_else(
+ || DEFAULT_SENDMAIL_PROGRAM.into()
+ );
+
let public_url = public_url
.trim_end_matches('/')
.into();
ServerConfig {
save_dir, command_socket, debug,
http_port, public_url, sse_wildcard_url, rocket_workers,
- template_dir, wasm_dir,
- log, bundled_sources, shapelibs,
+ template_dir, nwtemplate_dir, wasm_dir,
+ log, bundled_sources, shapelibs, sendmail,
}
}
}
&config().public_url.trim_end_matches("/"),
token.0);
let info = AccessTokenInfo { url };
- let report = access.deliver(&self.c.g, &gpl, &ipl, info)?;
+ let report = access.deliver(accounts, &self.c.g, &gpl, &ipl, info)?;
report
}
pub use std::io;
pub use std::io::ErrorKind;
pub use std::io::{BufReader, Read, BufRead, BufWriter, Write};
-pub use std::io::{SeekFrom};
+pub use std::io::{Seek, SeekFrom};
pub use std::iter::repeat_with;
pub use std::iter;
pub use std::marker::PhantomData;
pub use std::mem;
pub use std::num::{Wrapping, TryFromIntError};
pub use std::ops::{Deref, DerefMut};
-pub use std::os::unix::ffi::OsStrExt;
pub use std::os::unix;
+pub use std::os::unix::ffi::OsStrExt;
+pub use std::os::unix::process::CommandExt;
pub use std::path::PathBuf;
pub use std::process::{exit, Command};
pub use std::str::FromStr;
use crate::imports::*;
-use parking_lot::{const_rwlock, RwLock, MappedRwLockReadGuard};
+use parking_lot::{const_rwlock, RwLock, RwLockReadGuard};
+use parking_lot::{MappedRwLockReadGuard};
static STATE : RwLock<Option<State>> = const_rwlock(None);
#[throws(StartupError)]
pub fn init() {
- let guard = STATE.write();
+ let mut guard = STATE.write();
assert!(guard.is_none());
- let glob = format!("{}/*.tera", config().nwtemplates);
- *guard = State {
- tera: tera::Tera::new(&glob)?,
- };
+ let config = config();
+ let nwtemplate_dir = &config.nwtemplate_dir;
+ let glob = format!("{}/*.tera", nwtemplate_dir);
+ let tera = tera::Tera::new(&glob)
+ .map_err(|e| anyhow!("{}", e))
+ .context("load tamplates")
+ .with_context(|| nwtemplate_dir.to_string())?;
+
+ *guard = Some(State {
+ tera,
+ })
}
#[throws(tera::Error)]
-pub fn render<D: Serialize>(template_name: &str, data: &D) {
- fn get_st() -> MappedRwLockReadGuard<'static, State> {
- STATE.read().as_ref().unwrap()
+pub fn render<D: Serialize>(template_name: &str, data: &D) -> String {
+ fn get_tera() -> MappedRwLockReadGuard<'static, tera::Tera> {
+ let g = STATE.read();
+ RwLockReadGuard::map(g, |g| &g.as_ref().unwrap().tera)
}
- get_st().render(template_name, data)
+ get_tera().render(template_name, data)?
}
fn check_spec_permission(&self, _: Option<AuthorisationSuperuser>) {
}
fn deliver(&self,
+ ag: &AccountsGuard,
g: &Instance,
gpl: &GPlayerState,
ipl: &IPlayerState,
impl PlayerAccessSpec for PlayerAccessUnset {
#[throws(TokenDeliveryError)]
fn deliver(&self,
+ _ag: &AccountsGuard,
_g: &Instance,
_gpl: &GPlayerState,
_ipl: &IPlayerState,
}
#[throws(TokenDeliveryError)]
fn deliver(&self,
- _g: &Instance,
+ _ag: &AccountsGuard,
+ _g: &Instance,
_gpl: &GPlayerState,
_ipl: &IPlayerState,
_token: AccessTokenInfo) -> AccessTokenReport {
impl PlayerAccessSpec for UrlOnStdout {
#[throws(TDE)]
fn deliver<'t>(&self,
+ _ag: &AccountsGuard,
_g: &Instance,
_gpl: &GPlayerState,
_ipl: &IPlayerState,
impl PlayerAccessSpec for TokenByEmail {
#[throws(TDE)]
fn deliver<'t>(&self,
+ ag: &AccountsGuard,
g: &Instance,
gpl: &GPlayerState,
ipl: &IPlayerState,
nick: &gpl.nick,
};
- if self.addr.find(['\r','\n'] as &[char]).is_some() {
+ if self.addr.find((&['\r','\n']) as &[char]).is_some() {
throw!(anyhow!("email address may not contain line endings"));
}
- let message = match &ipl.account {
+ let (account, _) = ag.lookup(ipl.acctid).context("find account")?;
+ let account = &account.account;
+ let message = match &account.scope {
AS::Unix { user } => {
#[derive(Debug,Serialize)]
struct Data<'r> {
};
nwtemplates::render("token-unix", &data)
}
- other => {
+ _ => {
#[derive(Debug,Serialize)]
struct Data<'r> {
account: String,
common: CommonData<'r>,
};
let data = Data {
- account: other.to_string(),
+ account: account.to_string(),
common,
};
nwtemplates::render("token-other", &data)
}
- }.context("render email template")?;
+ }.map_err(|e| anyhow!(e.to_string()))
+ .context("render email template")?;
let messagefile = (||{
- let messagefile = tempfile::tempfile().context("tempfile")?;
+ let mut messagefile = tempfile::tempfile().context("tempfile")?;
messagefile.write_all(message.as_bytes()).context("write")?;
messagefile.flush().context("flush")?;
messagefile.seek(SeekFrom::Start(0)).context("seek")?;
- messagefile
+ Ok::<_,AE>(messagefile)
})().context("write email to temporary file.")?;
let sendmail = &config().sendmail;
- let command = Command::new(sendmail)
+ let mut command = Command::new(sendmail);
+ command
.args(&["-oee","-odb","-oi","-t","--"])
- .stdin(messagefile)
- .pre_exec(|| unsafe {
+ .stdin(messagefile);
+ unsafe {
+ command.pre_exec(|| {
// https://github.com/rust-lang/rust/issues/79731
let r = libc::dup2(2,1);
- assert_eq!(r, 1);
+ if r == 0 { Ok(()) }
+ else { Err(io::Error::last_os_error()) }
});
+ }
let st = command
.status()
- .with_context(!! format!("run sendmail ({})", sendmail))?;
+ .with_context(|| format!("run sendmail ({})", sendmail))?;
if !st.success() {
- throw!(format!("sendmail ({}) failed: {} ({})", sendmail, st, st));
+ throw!(anyhow!("sendmail ({}) failed: {} ({})", sendmail, st, st));
}
AccessTokenReport { lines: vec![