bundle auxiliary files (templates/css) in the binary
This commit is contained in:
parent
f50b81e8e2
commit
13f8e76ae3
6 changed files with 78 additions and 31 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,4 +3,5 @@ classification.json
|
|||
db.json
|
||||
api_token
|
||||
profile.json
|
||||
_*.json
|
||||
_*.json
|
||||
env
|
||||
|
|
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -694,6 +694,7 @@ dependencies = [
|
|||
"actix-web",
|
||||
"anyhow",
|
||||
"forgejo-api",
|
||||
"include_dir",
|
||||
"lazy_static",
|
||||
"lettre",
|
||||
"rand",
|
||||
|
@ -1260,6 +1261,25 @@ version = "0.1.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae21c3177a27788957044151cc2800043d127acaa460a47ebb9b84dfa2c6aa0"
|
||||
|
||||
[[package]]
|
||||
name = "include_dir"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
|
||||
dependencies = [
|
||||
"include_dir_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir_macros"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
|
|
|
@ -20,6 +20,7 @@ lazy_static = "1"
|
|||
actix-files = "0.6"
|
||||
unicode-segmentation = "1"
|
||||
lettre = { version = "0.11", features = ["builder", "smtp-transport", "rustls-tls"], default-features = false }
|
||||
include_dir = "0.7.4"
|
||||
|
||||
[profile.profiling]
|
||||
inherits = "dev"
|
||||
|
|
15
README.md
15
README.md
|
@ -1,4 +1,4 @@
|
|||
# spam management for forgejo
|
||||
# spam accounts management for forgejo
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
|||
## Configuration
|
||||
|
||||
Forgery reads the following environment variables:
|
||||
- `FORGEJO_URL`: url of the forgejo instance
|
||||
- `FORGEJO_URL`: url of the forgejo instance (e.g. https://git.deuxfleurs.fr)
|
||||
- `FORGEJO_API_TOKEN`: Forgejo API token *granting admin access*. Required. You
|
||||
can generate an API token using the Forgejo web interface in `Settings ->
|
||||
Applications -> Generate New Token`.
|
||||
|
@ -22,11 +22,11 @@ Forgery reads the following environment variables:
|
|||
locking accounts)
|
||||
- `ADMIN_CONTACT_EMAIL`: email that can be used to contact admins of your
|
||||
instance (included in the notification email sent when locking accounts)
|
||||
- `ACTUALLY_BAN_USERS`: define it (e.g. to `true`) to actually lock user
|
||||
accounts, send notification emails and eventually delete user accounts. If not
|
||||
defined (the default), no actual action is taken, spammers are only listed in
|
||||
the database. The variable should be set in production, but probably not for
|
||||
testing.
|
||||
- `ACTUALLY_BAN_USERS`: define it to `true` to actually lock user accounts, send
|
||||
notification emails and eventually delete user accounts. If not defined (the
|
||||
default) or set to `false`, no actual action is taken: spammers are only
|
||||
listed in the database. The variable should be set in production, but probably
|
||||
not for testing.
|
||||
|
||||
Environment variables that are relevant when `ACTUALLY_BAN_USERS=true`:
|
||||
- `SMTP_ADDRESS`: address of the SMTP relay used to send email notifications
|
||||
|
@ -42,4 +42,3 @@ Environment variables that are relevant when `ACTUALLY_BAN_USERS=true`:
|
|||
the email could not be sent…)
|
||||
- add backend to store data on garage instead of local files
|
||||
- improve error handling
|
||||
- bundle auxiliary files (templates, css) in the binary for easy deployment?
|
||||
|
|
52
src/main.rs
52
src/main.rs
|
@ -1,5 +1,5 @@
|
|||
use actix_web::{get, post, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
|
||||
use anyhow::Context;
|
||||
use anyhow::{anyhow, Context};
|
||||
use forgejo_api::{Auth, Forgejo};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::prelude::*;
|
||||
|
@ -61,9 +61,19 @@ impl Config {
|
|||
.context("reading the ADMIN_CONTACT_EMAIL environment variable")?;
|
||||
|
||||
let actually_ban = match std::env::var("ACTUALLY_BAN_USERS") {
|
||||
Ok(_) => ActuallyBan::Yes {
|
||||
smtp: SmtpConfig::from_env().await?,
|
||||
},
|
||||
Ok(s) => {
|
||||
if &s == "true" {
|
||||
ActuallyBan::Yes {
|
||||
smtp: SmtpConfig::from_env().await?,
|
||||
}
|
||||
} else if &s == "false" {
|
||||
ActuallyBan::No
|
||||
} else {
|
||||
return Err(anyhow!(
|
||||
"ACTUALLY_BAN_USERS: unknown value (expected: true/false)"
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(_) => ActuallyBan::No,
|
||||
};
|
||||
|
||||
|
@ -231,10 +241,25 @@ async fn apply_classification(
|
|||
}
|
||||
}
|
||||
|
||||
const TEMPLATES_DIR: include_dir::Dir = include_dir::include_dir!("templates");
|
||||
|
||||
lazy_static! {
|
||||
pub static ref TEMPLATES: Tera = {
|
||||
match Tera::new("templates/**/*.html") {
|
||||
Ok(t) => t,
|
||||
let files: Vec<_> = TEMPLATES_DIR
|
||||
.files()
|
||||
.into_iter()
|
||||
.map(|f| {
|
||||
(
|
||||
f.path().to_str().unwrap(),
|
||||
std::str::from_utf8(f.contents()).unwrap(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut tera = Tera::default();
|
||||
|
||||
match tera.add_raw_templates(files) {
|
||||
Ok(()) => tera,
|
||||
Err(e) => {
|
||||
eprintln!("Parsing error(s): {}", e);
|
||||
::std::process::exit(1);
|
||||
|
@ -421,6 +446,19 @@ async fn classified(
|
|||
HttpResponse::Ok().body(page)
|
||||
}
|
||||
|
||||
const STATIC_DIR: include_dir::Dir = include_dir::include_dir!("static");
|
||||
|
||||
#[get("/static/{filename:.*}")]
|
||||
async fn static_(req: HttpRequest) -> impl Responder {
|
||||
eprintln!("GET {}", req.uri());
|
||||
|
||||
let path: String = req.match_info().query("filename").parse().unwrap();
|
||||
match STATIC_DIR.get_file(path) {
|
||||
None => HttpResponse::NotFound().body("404 Not found"),
|
||||
Some(page) => HttpResponse::Ok().body(page.contents()),
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
eprintln!("Eval templates");
|
||||
|
@ -464,8 +502,8 @@ async fn main() -> anyhow::Result<()> {
|
|||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.service(actix_files::Files::new("/static/", "./static"))
|
||||
.app_data(st.clone())
|
||||
.service(static_)
|
||||
.service(index)
|
||||
.service(classified)
|
||||
.service(post_classified_index)
|
||||
|
|
|
@ -83,11 +83,7 @@ pub async fn refresh_user_data(
|
|||
|
||||
// Worker to delete spam accounts after their grace period expired
|
||||
|
||||
async fn try_purge_account(
|
||||
config: &Config,
|
||||
forge: &Forgejo,
|
||||
login: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
async fn try_purge_account(config: &Config, forge: &Forgejo, login: &str) -> anyhow::Result<()> {
|
||||
if let ActuallyBan::No = config.actually_ban {
|
||||
eprintln!("[Simulating: delete account of user {login}]");
|
||||
return Ok(());
|
||||
|
@ -104,11 +100,7 @@ async fn try_purge_account(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn purge_spammer_accounts(
|
||||
config: Arc<Config>,
|
||||
forge: Arc<Forgejo>,
|
||||
db: Arc<Mutex<Db>>,
|
||||
) {
|
||||
pub async fn purge_spammer_accounts(config: Arc<Config>, forge: Arc<Forgejo>, db: Arc<Mutex<Db>>) {
|
||||
loop {
|
||||
let mut classified_users = Vec::new();
|
||||
{
|
||||
|
@ -274,11 +266,7 @@ pub async fn try_lock_and_notify_user(
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn lock_and_notify_users(
|
||||
config: Arc<Config>,
|
||||
forge: Arc<Forgejo>,
|
||||
db: Arc<Mutex<Db>>,
|
||||
) {
|
||||
pub async fn lock_and_notify_users(config: Arc<Config>, forge: Arc<Forgejo>, db: Arc<Mutex<Db>>) {
|
||||
let mut spammers = Vec::new();
|
||||
{
|
||||
let db = &db.lock().unwrap();
|
||||
|
|
Loading…
Reference in a new issue