2025-03-21 01:38:07 -04:00
|
|
|
use pathbufd::PathBufD;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use std::fs;
|
|
|
|
use std::io::Result;
|
|
|
|
|
|
|
|
/// Security configuration.
|
|
|
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct SecurityConfig {
|
|
|
|
/// If registrations are enabled.
|
|
|
|
#[serde(default = "default_security_registration_enabled")]
|
|
|
|
pub registration_enabled: bool,
|
2025-04-03 15:07:57 -04:00
|
|
|
/// The name of the header which will contain the real IP of the connecting user.
|
2025-03-22 22:17:47 -04:00
|
|
|
#[serde(default = "default_real_ip_header")]
|
|
|
|
pub real_ip_header: String,
|
2025-03-21 01:38:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn default_security_registration_enabled() -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2025-03-22 22:17:47 -04:00
|
|
|
fn default_real_ip_header() -> String {
|
|
|
|
"CF-Connecting-IP".to_string()
|
|
|
|
}
|
|
|
|
|
2025-03-21 01:38:07 -04:00
|
|
|
impl Default for SecurityConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
registration_enabled: default_security_registration_enabled(),
|
2025-03-22 22:17:47 -04:00
|
|
|
real_ip_header: default_real_ip_header(),
|
2025-03-21 01:38:07 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Directories configuration.
|
|
|
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct DirsConfig {
|
|
|
|
/// HTML templates directory.
|
|
|
|
#[serde(default = "default_dir_templates")]
|
|
|
|
pub templates: String,
|
|
|
|
/// Static files directory.
|
|
|
|
#[serde(default = "default_dir_assets")]
|
|
|
|
pub assets: String,
|
2025-03-22 22:17:47 -04:00
|
|
|
/// Media (user avatars/banners) files directory.
|
|
|
|
#[serde(default = "default_dir_media")]
|
|
|
|
pub media: String,
|
2025-03-23 12:31:48 -04:00
|
|
|
/// The icons files directory.
|
|
|
|
#[serde(default = "default_dir_icons")]
|
|
|
|
pub icons: String,
|
2025-04-05 17:28:51 -04:00
|
|
|
/// The markdown document files directory.
|
|
|
|
#[serde(default = "default_dir_docs")]
|
|
|
|
pub docs: String,
|
2025-03-21 01:38:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn default_dir_templates() -> String {
|
|
|
|
"html".to_string()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_dir_assets() -> String {
|
|
|
|
"public".to_string()
|
|
|
|
}
|
|
|
|
|
2025-03-22 22:17:47 -04:00
|
|
|
fn default_dir_media() -> String {
|
|
|
|
"media".to_string()
|
|
|
|
}
|
|
|
|
|
2025-03-23 12:31:48 -04:00
|
|
|
fn default_dir_icons() -> String {
|
|
|
|
"icons".to_string()
|
|
|
|
}
|
|
|
|
|
2025-04-05 17:28:51 -04:00
|
|
|
fn default_dir_docs() -> String {
|
|
|
|
"docs".to_string()
|
|
|
|
}
|
|
|
|
|
2025-03-21 01:38:07 -04:00
|
|
|
impl Default for DirsConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
templates: default_dir_templates(),
|
|
|
|
assets: default_dir_assets(),
|
2025-03-22 22:17:47 -04:00
|
|
|
media: default_dir_media(),
|
2025-03-23 12:31:48 -04:00
|
|
|
icons: default_dir_icons(),
|
2025-04-05 17:28:51 -04:00
|
|
|
docs: default_dir_docs(),
|
2025-03-22 22:17:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Database configuration.
|
|
|
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct DatabaseConfig {
|
|
|
|
pub name: String,
|
|
|
|
#[cfg(feature = "postgres")]
|
|
|
|
pub url: String,
|
|
|
|
#[cfg(feature = "postgres")]
|
|
|
|
pub user: String,
|
|
|
|
#[cfg(feature = "postgres")]
|
|
|
|
pub password: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for DatabaseConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
name: "atto.db".to_string(),
|
|
|
|
#[cfg(feature = "postgres")]
|
|
|
|
url: "localhost:5432".to_string(),
|
|
|
|
#[cfg(feature = "postgres")]
|
|
|
|
user: "postgres".to_string(),
|
|
|
|
#[cfg(feature = "postgres")]
|
|
|
|
password: "postgres".to_string(),
|
2025-03-21 01:38:07 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-02 23:26:43 -04:00
|
|
|
/// Policies config (TOS/privacy)
|
|
|
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct PoliciesConfig {
|
|
|
|
/// The link to your terms of service page.
|
|
|
|
/// This is relative to `/auth/register` on the site.
|
|
|
|
///
|
|
|
|
/// If your TOS is an HTML file located in `./public`, you can put
|
|
|
|
/// `/public/tos.html` here (or something).
|
|
|
|
pub terms_of_service: String,
|
|
|
|
/// The link to your privacy policy page.
|
|
|
|
/// This is relative to `/auth/register` on the site.
|
|
|
|
///
|
|
|
|
/// Same deal as terms of service page.
|
|
|
|
pub privacy: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for PoliciesConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
terms_of_service: "/public/tos.html".to_string(),
|
|
|
|
privacy: "/public/privacy.html".to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Cloudflare Turnstile configuration
|
|
|
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct TurnstileConfig {
|
|
|
|
pub site_key: String,
|
|
|
|
pub secret_key: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TurnstileConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
site_key: "1x00000000000000000000AA".to_string(), // always passing, visible
|
|
|
|
secret_key: "1x0000000000000000000000000000000AA".to_string(), // always passing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-21 01:38:07 -04:00
|
|
|
/// Configuration file
|
|
|
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
|
|
pub struct Config {
|
2025-03-22 22:17:47 -04:00
|
|
|
/// The name of the app.
|
2025-03-21 01:38:07 -04:00
|
|
|
#[serde(default = "default_name")]
|
|
|
|
pub name: String,
|
2025-03-22 22:17:47 -04:00
|
|
|
/// The description of the app.
|
|
|
|
#[serde(default = "default_description")]
|
|
|
|
pub description: String,
|
|
|
|
/// The theme color of the app.
|
|
|
|
#[serde(default = "default_color")]
|
|
|
|
pub color: String,
|
2025-03-21 01:38:07 -04:00
|
|
|
/// The port to serve the server on.
|
|
|
|
#[serde(default = "default_port")]
|
|
|
|
pub port: u16,
|
2025-04-01 13:26:33 -04:00
|
|
|
/// A list of hosts which cannot be proxied through the image proxy.
|
|
|
|
///
|
|
|
|
/// They will return the default banner image instead of proxying.
|
|
|
|
///
|
|
|
|
/// It is recommended to put the host of your own public server in this list in
|
|
|
|
/// order to prevent a way too easy DOS.
|
|
|
|
#[serde(default = "default_banned_hosts")]
|
|
|
|
pub banned_hosts: Vec<String>,
|
2025-04-09 00:10:58 -04:00
|
|
|
/// The main public host of the server. **Not** used to check against banned hosts,
|
|
|
|
/// so this host should be included in there as well.
|
|
|
|
#[serde(default = "default_host")]
|
|
|
|
pub host: String,
|
2025-03-21 01:38:07 -04:00
|
|
|
/// Database security.
|
|
|
|
#[serde(default = "default_security")]
|
|
|
|
pub security: SecurityConfig,
|
|
|
|
/// The locations where different files should be matched.
|
|
|
|
#[serde(default = "default_dirs")]
|
|
|
|
pub dirs: DirsConfig,
|
2025-03-23 12:31:48 -04:00
|
|
|
/// Database configuration.
|
2025-03-22 22:17:47 -04:00
|
|
|
#[serde(default = "default_database")]
|
|
|
|
pub database: DatabaseConfig,
|
2025-03-23 12:31:48 -04:00
|
|
|
/// A list of files (just their name, no full path) which are NOT updated to match the
|
|
|
|
/// version built with the server binary.
|
|
|
|
#[serde(default = "default_no_track")]
|
|
|
|
pub no_track: Vec<String>,
|
2025-04-01 15:03:56 -04:00
|
|
|
/// A list of usernames which cannot be used. This also includes community names.
|
2025-03-31 22:35:11 -04:00
|
|
|
#[serde(default = "default_banned_usernames")]
|
|
|
|
pub banned_usernames: Vec<String>,
|
2025-04-02 23:26:43 -04:00
|
|
|
/// Configuration for your site's policies (terms of service, privacy).
|
|
|
|
#[serde(default = "default_policies")]
|
|
|
|
pub policies: PoliciesConfig,
|
|
|
|
/// Configuration for Cloudflare Turnstile.
|
|
|
|
#[serde(default = "default_turnstile")]
|
|
|
|
pub turnstile: TurnstileConfig,
|
2025-04-03 20:25:00 -04:00
|
|
|
/// The ID of the "town square" community. This community is required to allow
|
|
|
|
/// people to post from their profiles.
|
|
|
|
///
|
|
|
|
/// This community **must** have open write access.
|
|
|
|
#[serde(default)]
|
|
|
|
pub town_square: String,
|
2025-03-21 01:38:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn default_name() -> String {
|
|
|
|
"Tetratto".to_string()
|
|
|
|
}
|
|
|
|
|
2025-03-22 22:17:47 -04:00
|
|
|
fn default_description() -> String {
|
2025-04-03 15:07:57 -04:00
|
|
|
"🐇 tetratto!".to_string()
|
2025-03-21 01:38:07 -04:00
|
|
|
}
|
|
|
|
|
2025-03-22 22:17:47 -04:00
|
|
|
fn default_color() -> String {
|
|
|
|
"#c9b1bc".to_string()
|
|
|
|
}
|
|
|
|
fn default_port() -> u16 {
|
|
|
|
4118
|
2025-03-21 01:38:07 -04:00
|
|
|
}
|
|
|
|
|
2025-04-01 13:26:33 -04:00
|
|
|
fn default_banned_hosts() -> Vec<String> {
|
|
|
|
Vec::new()
|
|
|
|
}
|
|
|
|
|
2025-04-09 00:10:58 -04:00
|
|
|
fn default_host() -> String {
|
|
|
|
String::new()
|
|
|
|
}
|
|
|
|
|
2025-03-21 01:38:07 -04:00
|
|
|
fn default_security() -> SecurityConfig {
|
|
|
|
SecurityConfig::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_dirs() -> DirsConfig {
|
|
|
|
DirsConfig::default()
|
|
|
|
}
|
|
|
|
|
2025-03-22 22:17:47 -04:00
|
|
|
fn default_database() -> DatabaseConfig {
|
|
|
|
DatabaseConfig::default()
|
|
|
|
}
|
|
|
|
|
2025-03-23 12:31:48 -04:00
|
|
|
fn default_no_track() -> Vec<String> {
|
|
|
|
Vec::new()
|
|
|
|
}
|
|
|
|
|
2025-03-31 22:35:11 -04:00
|
|
|
fn default_banned_usernames() -> Vec<String> {
|
|
|
|
vec![
|
|
|
|
"admin".to_string(),
|
|
|
|
"owner".to_string(),
|
|
|
|
"moderator".to_string(),
|
|
|
|
"api".to_string(),
|
|
|
|
"communities".to_string(),
|
|
|
|
"notifs".to_string(),
|
|
|
|
"notification".to_string(),
|
|
|
|
"post".to_string(),
|
2025-04-01 15:03:56 -04:00
|
|
|
"void".to_string(),
|
2025-03-31 22:35:11 -04:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2025-04-02 23:26:43 -04:00
|
|
|
fn default_policies() -> PoliciesConfig {
|
|
|
|
PoliciesConfig::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_turnstile() -> TurnstileConfig {
|
|
|
|
TurnstileConfig::default()
|
|
|
|
}
|
|
|
|
|
2025-03-21 01:38:07 -04:00
|
|
|
impl Default for Config {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
name: default_name(),
|
2025-03-22 22:17:47 -04:00
|
|
|
description: default_description(),
|
|
|
|
color: default_color(),
|
2025-03-21 01:38:07 -04:00
|
|
|
port: default_port(),
|
2025-04-01 13:26:33 -04:00
|
|
|
banned_hosts: default_banned_hosts(),
|
2025-04-09 00:10:58 -04:00
|
|
|
host: default_host(),
|
2025-03-21 01:38:07 -04:00
|
|
|
database: default_database(),
|
|
|
|
security: default_security(),
|
|
|
|
dirs: default_dirs(),
|
2025-03-23 12:31:48 -04:00
|
|
|
no_track: default_no_track(),
|
2025-03-31 22:35:11 -04:00
|
|
|
banned_usernames: default_banned_usernames(),
|
2025-04-02 23:26:43 -04:00
|
|
|
policies: default_policies(),
|
|
|
|
turnstile: default_turnstile(),
|
2025-04-03 20:25:00 -04:00
|
|
|
town_square: String::new(),
|
2025-03-21 01:38:07 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Config {
|
|
|
|
/// Read configuration file into [`Config`]
|
|
|
|
pub fn read(contents: String) -> Self {
|
|
|
|
toml::from_str::<Self>(&contents).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Pull configuration file
|
|
|
|
pub fn get_config() -> Self {
|
|
|
|
let path = PathBufD::current().join("tetratto.toml");
|
|
|
|
|
|
|
|
match fs::read_to_string(&path) {
|
|
|
|
Ok(c) => Config::read(c),
|
|
|
|
Err(_) => {
|
|
|
|
Self::update_config(Self::default()).expect("failed to write default config");
|
|
|
|
Self::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update configuration file
|
|
|
|
pub fn update_config(contents: Self) -> Result<()> {
|
|
|
|
let c = fs::canonicalize(".").unwrap();
|
|
|
|
let here = c.to_str().unwrap();
|
|
|
|
|
|
|
|
fs::write(
|
|
|
|
format!("{here}/tetratto.toml"),
|
|
|
|
toml::to_string_pretty::<Self>(&contents).unwrap(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|