env vars handling code: slight cleanup

This commit is contained in:
Armaël Guéneau 2025-01-03 12:01:47 +01:00
parent 5614796e71
commit 812eee1a5f
4 changed files with 49 additions and 54 deletions

View file

@ -14,34 +14,33 @@
## Configuration ## Configuration
Forgery reads the following environment variables: Forgery reads the following environment variables:
- `FORGE_URL`: url of the forgejo instance (e.g. https://git.deuxfleurs.fr) - `FORGE_URL` (**mandatory**): url of the forgejo instance (e.g.
- `FORGE_API_TOKEN`: Forgejo API token *granting admin access*. Required. You https://git.deuxfleurs.fr)
can generate an API token using the Forgejo web interface in `Settings -> - `FORGE_API_TOKEN` (**mandatory**): Forgejo API token *granting admin access*.
You can generate an API token using the Forgejo web interface in `Settings ->
Applications -> Generate New Token`. Applications -> Generate New Token`.
- `ORG_NAME`: organization name (used in the notification email sent when - `ACTUALLY_BAN_USERS` (default: `false`): define it to `true` to actually lock
locking accounts) user accounts, send notification emails and eventually delete user accounts.
- `ADMIN_CONTACT_EMAIL`: email that can be used to contact admins of your Otherwise, no actual action is taken: spammers are only listed in the
instance (included in the notification email sent when locking accounts) database. The variable should be set in production, but probably not for
- `ACTUALLY_BAN_USERS`: define it to `true` to actually lock user accounts, send testing.
notification emails and eventually delete user accounts. If not defined (the - `STORAGE_BACKEND` (default: `local`): either `local` or `s3`. Chose `local` to
default) or set to `false`, no actual action is taken: spammers are only store the application state to local files, or `s3` to store them in
listed in the database. The variable should be set in production, but probably S3-compatible storage (see below for corresponding configuration variables).
not for testing. - `BIND_ADDR` (default: `127.0.0.1:8080`): address on which the webserver listens
- `STORAGE_BACKEND`: either `local` (default) or `s3`. Chose `local` to store
the application state to local files, or `s3` to store them in S3-compatible
storage (see below for corresponding configuration variables).
- `LISTEN_ADDR`: address on which the webserver listens (default: `0.0.0.0`)
- `LISTEN_PORT`: port on which the webserver listens (default: `8080`)
Environment variables read when `ACTUALLY_BAN_USERS=true`: Environment variables read when `ACTUALLY_BAN_USERS=true`:
- `SMTP_ADDRESS`: address of the SMTP relay used to send email notifications - `SMTP_ADDRESS`: address of the SMTP relay used to send email notifications
- `SMTP_USERNAME`: SMTP username - `SMTP_USERNAME`: SMTP username
- `SMTP_PASSWORD`: SMTP password - `SMTP_PASSWORD`: SMTP password
- `ADMIN_CONTACT_EMAIL`: email that can be used to contact admins of your
instance (included in the notification email sent when locking accounts)
- `ORG_NAME`: organization name (used in the notification email sent when
locking accounts)
Environment variables read when `STORAGE_BACKEND=local`: Environment variables read when `STORAGE_BACKEND=local`:
- `STORAGE_LOCAL_DIR`: path to a local directory where to store the application - `STORAGE_LOCAL_DIR` (default: `.`): path to a local directory where to store
data (as two files `db.json` and `model.json`). Defaults to `.` if not the application data (as two files `db.json` and `model.json`).
defined.
Environment variables read when `STORAGE_BACKEND=s3`: Environment variables read when `STORAGE_BACKEND=s3`:
- `STORAGE_S3_BUCKET`: name of the bucket where to store the application data - `STORAGE_S3_BUCKET`: name of the bucket where to store the application data

View file

@ -39,13 +39,13 @@ impl SmtpConfig {
pub async fn send_locked_account_notice( pub async fn send_locked_account_notice(
config: &Config, config: &Config,
smtp: &SmtpConfig, smtp: &SmtpConfig,
admin_contact_email: &str,
org_name: &str,
login: &str, login: &str,
email: &str, email: &str,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let grace_period_days = crate::GRACE_PERIOD.as_secs() / (24 * 3600); let grace_period_days = crate::GRACE_PERIOD.as_secs() / (24 * 3600);
let org_name = &config.org_name;
let forge_url = &config.forge_url; let forge_url = &config.forge_url;
let admin_contact_email = &config.admin_contact_email;
let email = Message::builder() let email = Message::builder()
.from(smtp.username.parse().unwrap()) .from(smtp.username.parse().unwrap())

View file

@ -56,23 +56,20 @@ const GUESS_LEGIT_THRESHOLD: f32 = 0.3;
pub struct Config { pub struct Config {
pub forge_url: Url, pub forge_url: Url,
pub forge_api_token: String, pub forge_api_token: String,
pub org_name: String,
pub admin_contact_email: String,
pub actually_ban: ActuallyBan, pub actually_ban: ActuallyBan,
pub listen_addr: String, pub bind_addr: String,
pub listen_port: u16,
} }
impl Config { impl Config {
async fn from_env() -> anyhow::Result<Self> { async fn from_env() -> anyhow::Result<Self> {
let forge_url_s = env_var("FORGE_URL")?; let forge_url_s = env_var("FORGE_URL")?;
let forge_api_token = env_var("FORGE_API_TOKEN")?; let forge_api_token = env_var("FORGE_API_TOKEN")?;
let org_name = env_var("ORG_NAME")?;
let admin_contact_email = env_var("ADMIN_CONTACT_EMAIL")?;
let actually_ban = match env_var("ACTUALLY_BAN_USERS").as_deref() { let actually_ban = match env_var("ACTUALLY_BAN_USERS").as_deref() {
Ok("true") => ActuallyBan::Yes { Ok("true") => ActuallyBan::Yes {
smtp: SmtpConfig::from_env().await?, smtp: SmtpConfig::from_env().await?,
admin_contact_email: env_var("ADMIN_CONTACT_EMAIL")?,
org_name: env_var("ORG_NAME")?,
}, },
Ok("false") => ActuallyBan::No, Ok("false") => ActuallyBan::No,
Ok(_) => { Ok(_) => {
@ -83,21 +80,13 @@ impl Config {
Err(_) => ActuallyBan::No, Err(_) => ActuallyBan::No,
}; };
let listen_addr = env_var("LISTEN_ADDR").unwrap_or(String::from("0.0.0.0")); let bind_addr = env_var("BIND_ADDR").unwrap_or(String::from("127.0.0.1:8080"));
let listen_port: u16 = match env_var("LISTEN_PORT").map(|s| u16::from_str_radix(&s, 10)) {
Ok(Err(e)) => return Err(anyhow!("LISTEN_PORT: invalid value ({})", e)),
Ok(Ok(p)) => p,
Err(_) => 8080,
};
Ok(Config { Ok(Config {
forge_url: Url::parse(&forge_url_s).context("parsing FORGE_URL")?, forge_url: Url::parse(&forge_url_s).context("parsing FORGE_URL")?,
forge_api_token, forge_api_token,
org_name,
admin_contact_email,
actually_ban, actually_ban,
listen_addr, bind_addr,
listen_port,
}) })
} }
} }
@ -105,7 +94,11 @@ impl Config {
// Whether we are actually banning users or are instead in "testing" mode where // Whether we are actually banning users or are instead in "testing" mode where
// we don't do anything. (Defaults to "No".) // we don't do anything. (Defaults to "No".)
pub enum ActuallyBan { pub enum ActuallyBan {
Yes { smtp: SmtpConfig }, Yes {
smtp: SmtpConfig,
admin_contact_email: String,
org_name: String,
},
No, No,
} }
@ -496,15 +489,7 @@ async fn main() -> anyhow::Result<()> {
.spawn(async move { workers::lock_and_notify_users(config, storage, forge, db).await }) .spawn(async move { workers::lock_and_notify_users(config, storage, forge, db).await })
}; };
println!( println!("Listening on http://{}", &config.bind_addr);
"Listening on http://{}:{}",
(if config.listen_addr == "0.0.0.0" {
"127.0.0.1"
} else {
&config.listen_addr
}),
config.listen_port
);
let app = Router::new() let app = Router::new()
.route("/", get(get_index).post(post_classified_index)) .route("/", get(get_index).post(post_classified_index))
@ -516,8 +501,7 @@ async fn main() -> anyhow::Result<()> {
.with_state(shared_state); .with_state(shared_state);
let webserver = { let webserver = {
let listener = let listener = tokio::net::TcpListener::bind(&config.bind_addr)
tokio::net::TcpListener::bind((config.listen_addr.clone(), config.listen_port))
.await .await
.unwrap(); .unwrap();
axum::serve(listener, app) axum::serve(listener, app)

View file

@ -237,9 +237,21 @@ pub async fn try_lock_and_notify_user(
if !notified { if !notified {
match &config.actually_ban { match &config.actually_ban {
ActuallyBan::Yes { smtp } => { ActuallyBan::Yes {
smtp,
admin_contact_email,
org_name,
} => {
eprintln!("Sending notification email to user {login}"); eprintln!("Sending notification email to user {login}");
email::send_locked_account_notice(config, smtp, &login, &email).await?; email::send_locked_account_notice(
config,
smtp,
&admin_contact_email,
&org_name,
&login,
&email,
)
.await?;
eprintln!("Success"); eprintln!("Success");
} }
ActuallyBan::No => { ActuallyBan::No => {