diff --git a/Dockerfile b/Dockerfile index ac4a995..c8bd2ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,12 +20,23 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \ fi && \ cp target/release/tranquil-pds /tmp/tranquil-pds -FROM alpine:3.23 -RUN apk add --no-cache msmtp ca-certificates && ln -sf /usr/bin/msmtp /usr/sbin/sendmail +FROM alpine:3.23 AS signal-cli +RUN apk add --no-cache curl tar +ARG SIGNAL_CLI_VERSION=0.13.24 +RUN curl -fsSL "https://github.com/AsamK/signal-cli/releases/download/v${SIGNAL_CLI_VERSION}/signal-cli-${SIGNAL_CLI_VERSION}-Linux-native.tar.gz" \ + | tar xz -C /usr/local/bin + +FROM debian:trixie-slim +RUN apt-get update && apt-get install -y --no-install-recommends msmtp ca-certificates \ + && rm -rf /var/lib/apt/lists/* \ + && ln -sf /usr/bin/msmtp /usr/sbin/sendmail +COPY --from=signal-cli /usr/local/bin/signal-cli /usr/local/bin/signal-cli +VOLUME /var/lib/signal-cli COPY --from=builder /tmp/tranquil-pds /usr/local/bin/tranquil-pds COPY --from=frontend /app/dist /var/lib/tranquil-pds/frontend COPY migrations /app/migrations WORKDIR /app +ENV SIGNAL_CLI_CONFIG=/var/lib/signal-cli ENV SERVER_HOST=0.0.0.0 ENV SERVER_PORT=3000 EXPOSE 3000 diff --git a/crates/tranquil-comms/src/sender.rs b/crates/tranquil-comms/src/sender.rs index 0380a9f..84a2e5f 100644 --- a/crates/tranquil-comms/src/sender.rs +++ b/crates/tranquil-comms/src/sender.rs @@ -22,10 +22,13 @@ pub trait CommsSender: Send + Sync { #[derive(Debug, thiserror::Error)] pub enum SendError { - #[error("Failed to spawn sendmail process: {0}")] - ProcessSpawn(#[from] std::io::Error), - #[error("Sendmail exited with non-zero status: {0}")] - SendmailFailed(String), + #[error("Failed to spawn {command}: {source}")] + ProcessSpawn { + command: String, + source: std::io::Error, + }, + #[error("{command} exited with non-zero status: {detail}")] + ProcessFailed { command: String, detail: String }, #[error("Channel not configured: {0:?}")] NotConfigured(CommsChannel), #[error("External service error: {0}")] @@ -160,14 +163,31 @@ impl CommsSender for EmailSender { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) - .spawn()?; + .spawn() + .map_err(|e| SendError::ProcessSpawn { + command: self.sendmail_path.clone(), + source: e, + })?; if let Some(mut stdin) = child.stdin.take() { - stdin.write_all(email_content.as_bytes()).await?; + stdin.write_all(email_content.as_bytes()).await.map_err(|e| { + SendError::ProcessSpawn { + command: self.sendmail_path.clone(), + source: e, + } + })?; } - let output = child.wait_with_output().await?; + let output = child.wait_with_output().await.map_err(|e| { + SendError::ProcessSpawn { + command: self.sendmail_path.clone(), + source: e, + } + })?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); - return Err(SendError::SendmailFailed(stderr.to_string())); + return Err(SendError::ProcessFailed { + command: self.sendmail_path.clone(), + detail: stderr.to_string(), + }); } Ok(()) } @@ -656,7 +676,10 @@ impl CommsSender for SignalSender { retry_delay(attempt).await; continue; } - return Err(SendError::ProcessSpawn(e)); + return Err(SendError::ProcessSpawn { + command: self.signal_cli_path.clone(), + source: e, + }); } Err(_) => { if attempt < MAX_RETRIES - 1 {