165 lines
5.2 KiB
Rust
165 lines
5.2 KiB
Rust
mod admin;
|
|
mod i18n;
|
|
mod migrations;
|
|
pub mod models;
|
|
mod public;
|
|
mod telegram;
|
|
mod turnstile;
|
|
mod tz;
|
|
mod uploads;
|
|
mod web_push;
|
|
|
|
use tracing_subscriber;
|
|
|
|
use cot::cli::CliMetadata;
|
|
use cot::config::{
|
|
DatabaseConfig, MiddlewareConfig, ProjectConfig, SameSite, SessionMiddlewareConfig,
|
|
SessionStoreConfig, SessionStoreTypeConfig,
|
|
};
|
|
use cot::db::migrations::SyncDynMigration;
|
|
use cot::middleware::SessionMiddleware;
|
|
use cot::project::{MiddlewareContext, ProjectContext, RegisterAppsContext, RootHandler};
|
|
use cot::router::Router;
|
|
use cot::session::db::SessionApp;
|
|
use cot::{App, AppBuilder, Project};
|
|
|
|
struct PettingApp;
|
|
|
|
impl App for PettingApp {
|
|
fn name(&self) -> &'static str {
|
|
"web-petting"
|
|
}
|
|
|
|
fn migrations(&self) -> Vec<Box<SyncDynMigration>> {
|
|
cot::db::migrations::wrap_migrations(migrations::MIGRATIONS)
|
|
}
|
|
|
|
fn router(&self) -> Router {
|
|
admin::admin_router()
|
|
}
|
|
}
|
|
|
|
struct PublicApp;
|
|
|
|
#[async_trait::async_trait]
|
|
impl App for PublicApp {
|
|
fn name(&self) -> &'static str {
|
|
"public"
|
|
}
|
|
|
|
async fn init(&self, context: &mut ProjectContext) -> cot::Result<()> {
|
|
web_push::initialize(context.database()).await;
|
|
Ok(())
|
|
}
|
|
|
|
fn router(&self) -> Router {
|
|
public::public_router()
|
|
}
|
|
}
|
|
|
|
struct PettingProject;
|
|
|
|
fn parse_bool_env(name: &str) -> Option<bool> {
|
|
let value = std::env::var(name).ok()?;
|
|
match value.trim().to_ascii_lowercase().as_str() {
|
|
"1" | "true" | "yes" | "on" => Some(true),
|
|
"0" | "false" | "no" | "off" => Some(false),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn debug_enabled(config_name: &str) -> bool {
|
|
parse_bool_env("WEB_PETTING_DEBUG").unwrap_or_else(|| {
|
|
matches!(
|
|
config_name,
|
|
"dev" | "development" | "debug" | "local" | "test"
|
|
)
|
|
})
|
|
}
|
|
|
|
fn database_url() -> String {
|
|
std::env::var("WEB_PETTING_DATABASE_URL")
|
|
.or_else(|_| std::env::var("DATABASE_URL"))
|
|
.unwrap_or_else(|_| {
|
|
eprintln!(
|
|
"WEB_PETTING_DATABASE_URL and DATABASE_URL are not set; using the local default \
|
|
postgresql://postgres:postgres@localhost:5432/web_petting"
|
|
);
|
|
"postgresql://postgres:postgres@localhost:5432/web_petting".to_string()
|
|
})
|
|
}
|
|
|
|
impl Project for PettingProject {
|
|
fn cli_metadata(&self) -> CliMetadata {
|
|
cot::cli::metadata!()
|
|
}
|
|
|
|
fn config(&self, config_name: &str) -> cot::Result<ProjectConfig> {
|
|
Ok(ProjectConfig::builder()
|
|
.debug(debug_enabled(config_name))
|
|
.database(DatabaseConfig::builder().url(database_url()).build())
|
|
.middlewares(
|
|
MiddlewareConfig::builder()
|
|
.session(
|
|
SessionMiddlewareConfig::builder()
|
|
.secure(false)
|
|
.same_site(SameSite::Lax)
|
|
.store(
|
|
SessionStoreConfig::builder()
|
|
.store_type(SessionStoreTypeConfig::Database)
|
|
.build(),
|
|
)
|
|
.build(),
|
|
)
|
|
.build(),
|
|
)
|
|
.build())
|
|
}
|
|
|
|
fn register_apps(&self, apps: &mut AppBuilder, _context: &RegisterAppsContext) {
|
|
apps.register(SessionApp::new());
|
|
apps.register_with_views(PublicApp, "");
|
|
apps.register_with_views(PettingApp, "/admin");
|
|
}
|
|
|
|
fn middlewares(
|
|
&self,
|
|
handler: cot::project::RootHandlerBuilder,
|
|
context: &MiddlewareContext,
|
|
) -> RootHandler {
|
|
handler
|
|
.middleware(SessionMiddleware::from_context(context))
|
|
.build()
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"));
|
|
let _ = tracing_subscriber::fmt().with_env_filter(filter).try_init();
|
|
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.expect("failed to build Tokio runtime");
|
|
if let Err(error) = runtime.block_on(cot::run_cli(PettingProject)) {
|
|
let message = error.to_string();
|
|
let details = format!("{error:?}");
|
|
eprintln!("Failed to start web-petting: {message}\nDetails: {details}");
|
|
if details.contains("28P01") || details.contains("password authentication failed") {
|
|
eprintln!(
|
|
"\nPostgreSQL rejected the configured username or password.\n\
|
|
Set the connection string before starting the application, for example:\n\n \
|
|
WEB_PETTING_DATABASE_URL='postgresql://postgres:YOUR_PASSWORD@localhost:5432/web_petting' cargo run\n\n\
|
|
WEB_PETTING_DATABASE_URL takes priority over DATABASE_URL.\n\
|
|
Check the current value with: printenv WEB_PETTING_DATABASE_URL"
|
|
);
|
|
} else if message.to_ascii_lowercase().contains("database") {
|
|
eprintln!(
|
|
"\nConfigure PostgreSQL with WEB_PETTING_DATABASE_URL or DATABASE_URL.\n\
|
|
Example:\n\n WEB_PETTING_DATABASE_URL='postgresql://postgres:YOUR_PASSWORD@localhost:5432/web_petting' cargo run"
|
|
);
|
|
}
|
|
std::process::exit(1);
|
|
}
|
|
}
|