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
|
db.json
|
||||||
api_token
|
api_token
|
||||||
profile.json
|
profile.json
|
||||||
_*.json
|
_*.json
|
||||||
|
env
|
||||||
|
|
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -694,6 +694,7 @@ dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"forgejo-api",
|
"forgejo-api",
|
||||||
|
"include_dir",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lettre",
|
"lettre",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -1260,6 +1261,25 @@ version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aae21c3177a27788957044151cc2800043d127acaa460a47ebb9b84dfa2c6aa0"
|
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]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
|
|
|
@ -20,6 +20,7 @@ lazy_static = "1"
|
||||||
actix-files = "0.6"
|
actix-files = "0.6"
|
||||||
unicode-segmentation = "1"
|
unicode-segmentation = "1"
|
||||||
lettre = { version = "0.11", features = ["builder", "smtp-transport", "rustls-tls"], default-features = false }
|
lettre = { version = "0.11", features = ["builder", "smtp-transport", "rustls-tls"], default-features = false }
|
||||||
|
include_dir = "0.7.4"
|
||||||
|
|
||||||
[profile.profiling]
|
[profile.profiling]
|
||||||
inherits = "dev"
|
inherits = "dev"
|
||||||
|
|
15
README.md
15
README.md
|
@ -1,4 +1,4 @@
|
||||||
# spam management for forgejo
|
# spam accounts management for forgejo
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Forgery reads the following environment variables:
|
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
|
- `FORGEJO_API_TOKEN`: Forgejo API token *granting admin access*. Required. You
|
||||||
can generate an API token using the Forgejo web interface in `Settings ->
|
can generate an API token using the Forgejo web interface in `Settings ->
|
||||||
Applications -> Generate New Token`.
|
Applications -> Generate New Token`.
|
||||||
|
@ -22,11 +22,11 @@ Forgery reads the following environment variables:
|
||||||
locking accounts)
|
locking accounts)
|
||||||
- `ADMIN_CONTACT_EMAIL`: email that can be used to contact admins of your
|
- `ADMIN_CONTACT_EMAIL`: email that can be used to contact admins of your
|
||||||
instance (included in the notification email sent when locking accounts)
|
instance (included in the notification email sent when locking accounts)
|
||||||
- `ACTUALLY_BAN_USERS`: define it (e.g. to `true`) to actually lock user
|
- `ACTUALLY_BAN_USERS`: define it to `true` to actually lock user accounts, send
|
||||||
accounts, send notification emails and eventually delete user accounts. If not
|
notification emails and eventually delete user accounts. If not defined (the
|
||||||
defined (the default), no actual action is taken, spammers are only listed in
|
default) or set to `false`, no actual action is taken: spammers are only
|
||||||
the database. The variable should be set in production, but probably not for
|
listed in the database. The variable should be set in production, but probably
|
||||||
testing.
|
not for testing.
|
||||||
|
|
||||||
Environment variables that are relevant when `ACTUALLY_BAN_USERS=true`:
|
Environment variables that are relevant 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
|
||||||
|
@ -42,4 +42,3 @@ Environment variables that are relevant when `ACTUALLY_BAN_USERS=true`:
|
||||||
the email could not be sent…)
|
the email could not be sent…)
|
||||||
- add backend to store data on garage instead of local files
|
- add backend to store data on garage instead of local files
|
||||||
- improve error handling
|
- 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 actix_web::{get, post, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
|
||||||
use anyhow::Context;
|
use anyhow::{anyhow, Context};
|
||||||
use forgejo_api::{Auth, Forgejo};
|
use forgejo_api::{Auth, Forgejo};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
@ -61,9 +61,19 @@ impl Config {
|
||||||
.context("reading the ADMIN_CONTACT_EMAIL environment variable")?;
|
.context("reading the ADMIN_CONTACT_EMAIL environment variable")?;
|
||||||
|
|
||||||
let actually_ban = match std::env::var("ACTUALLY_BAN_USERS") {
|
let actually_ban = match std::env::var("ACTUALLY_BAN_USERS") {
|
||||||
Ok(_) => ActuallyBan::Yes {
|
Ok(s) => {
|
||||||
smtp: SmtpConfig::from_env().await?,
|
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,
|
Err(_) => ActuallyBan::No,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -231,10 +241,25 @@ async fn apply_classification(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TEMPLATES_DIR: include_dir::Dir = include_dir::include_dir!("templates");
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref TEMPLATES: Tera = {
|
pub static ref TEMPLATES: Tera = {
|
||||||
match Tera::new("templates/**/*.html") {
|
let files: Vec<_> = TEMPLATES_DIR
|
||||||
Ok(t) => t,
|
.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) => {
|
Err(e) => {
|
||||||
eprintln!("Parsing error(s): {}", e);
|
eprintln!("Parsing error(s): {}", e);
|
||||||
::std::process::exit(1);
|
::std::process::exit(1);
|
||||||
|
@ -421,6 +446,19 @@ async fn classified(
|
||||||
HttpResponse::Ok().body(page)
|
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]
|
#[actix_web::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
eprintln!("Eval templates");
|
eprintln!("Eval templates");
|
||||||
|
@ -464,8 +502,8 @@ async fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.service(actix_files::Files::new("/static/", "./static"))
|
|
||||||
.app_data(st.clone())
|
.app_data(st.clone())
|
||||||
|
.service(static_)
|
||||||
.service(index)
|
.service(index)
|
||||||
.service(classified)
|
.service(classified)
|
||||||
.service(post_classified_index)
|
.service(post_classified_index)
|
||||||
|
|
|
@ -83,11 +83,7 @@ pub async fn refresh_user_data(
|
||||||
|
|
||||||
// Worker to delete spam accounts after their grace period expired
|
// Worker to delete spam accounts after their grace period expired
|
||||||
|
|
||||||
async fn try_purge_account(
|
async fn try_purge_account(config: &Config, forge: &Forgejo, login: &str) -> anyhow::Result<()> {
|
||||||
config: &Config,
|
|
||||||
forge: &Forgejo,
|
|
||||||
login: &str,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
if let ActuallyBan::No = config.actually_ban {
|
if let ActuallyBan::No = config.actually_ban {
|
||||||
eprintln!("[Simulating: delete account of user {login}]");
|
eprintln!("[Simulating: delete account of user {login}]");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -104,11 +100,7 @@ async fn try_purge_account(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn purge_spammer_accounts(
|
pub async fn purge_spammer_accounts(config: Arc<Config>, forge: Arc<Forgejo>, db: Arc<Mutex<Db>>) {
|
||||||
config: Arc<Config>,
|
|
||||||
forge: Arc<Forgejo>,
|
|
||||||
db: Arc<Mutex<Db>>,
|
|
||||||
) {
|
|
||||||
loop {
|
loop {
|
||||||
let mut classified_users = Vec::new();
|
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(
|
pub async fn lock_and_notify_users(config: Arc<Config>, forge: Arc<Forgejo>, db: Arc<Mutex<Db>>) {
|
||||||
config: Arc<Config>,
|
|
||||||
forge: Arc<Forgejo>,
|
|
||||||
db: Arc<Mutex<Db>>,
|
|
||||||
) {
|
|
||||||
let mut spammers = Vec::new();
|
let mut spammers = Vec::new();
|
||||||
{
|
{
|
||||||
let db = &db.lock().unwrap();
|
let db = &db.lock().unwrap();
|
||||||
|
|
Loading…
Reference in a new issue