#![allow(dead_code)]
mod aska;
mod certgen;
mod events;
mod hookstr;
mod keyutil;
mod nips;
mod nostru;
mod rendering;
mod routes;
pub mod storage;
mod ts;
mod urls;
mod user;
mod util;
use clap::Arg;
use qpidfile::Pidfile;
use standard_paths::{LocationType, StandardPaths};
use std::path::PathBuf;
use routes::guardian;
use hookstr::Hookstr;
use windmark_titanesque::response::Response;
use nostr_sdk::prelude::*;
rust_i18n::i18n!("locales");
pub fn parse_args() -> clap::ArgMatches {
clap::Command::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
.arg(
Arg::new("port")
.default_value("1965")
.value_parser(clap::value_parser!(i32))
.short('p')
.help("Gemini service port number"),
)
.arg(
Arg::new("pid-file")
.default_value("caracal.pid")
.long("pid-file")
.help("Process id (pid) file path"),
)
.get_matches()
}
macro_rules! mkroute {
($handler:expr) => {
move |ctx| guardian(ctx, $handler)
};
}
#[windmark_titanesque::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let sp = StandardPaths::without_org(env!("CARGO_PKG_NAME"));
let matches = parse_args();
let port = matches.get_one::<i32>("port").copied().unwrap();
let pid_file_path =
PathBuf::from(matches.get_one::<String>("pid-file").unwrap());
let _pidfile = Pidfile::new(pid_file_path);
let app_data_path = sp
.writable_location(LocationType::AppLocalDataLocation)
.expect("Cannot find a writable location for data files");
let gemini_data_path = app_data_path.join("gemini");
std::fs::create_dir_all(gemini_data_path.clone())?;
let cert_path = gemini_data_path.join("cert.pem");
let key_path = gemini_data_path.join("key.pem");
if !cert_path.exists() || !key_path.exists() {
certgen::gen_cert(
vec!["localhost".to_string()],
&cert_path,
&key_path,
)?;
}
windmark_titanesque::router::Router::new()
.set_port(port)
.set_private_key_file(key_path.display().to_string())
.set_certificate_file(cert_path.display().to_string())
.attach_async(Hookstr::default())
.mount("/", mkroute!(routes::index))
.mount("/contacts", mkroute!(routes::contact_list_index))
.mount("/connect", routes::nostr_connect_index)
.mount("/connect/bunker", routes::nostr_connect_change)
.mount("/search/notes", routes::search_content)
.mount("/search/notes/:searchq", routes::search_content)
.mount("/search/notes/:searchq/:page", routes::search_content)
.mount("/feed", mkroute!(routes::feed))
.mount("/feed/:limit", mkroute!(routes::feed))
.mount("/follow", routes::follow)
.mount("/follow/:npub", routes::follow)
.mount("/hashtags", mkroute!(routes::hashtags_index))
.mount("/hashtags/:tag/list", mkroute!(routes::notes_by_hashtag))
.mount("/hashtags/:tag/notes", mkroute!(routes::notes_by_hashtag))
.mount("/post", mkroute!(routes::post_note))
.mount("/post/pow/:pow", mkroute!(routes::post_note))
.mount("/profile", routes::profile_index)
.mount("/profile/upload/picture", routes::profile_upload)
.mount("/profile/upload/banner", routes::profile_upload)
.mount("/profile/edit/:attr", routes::profile_edit)
.mount("/profile/reset", routes::profile_init_reset)
.mount("/profile/init", routes::profile_init_reset)
.mount("/p/:npub", mkroute!(routes::person_profile))
.mount("/p/:npub/follow", mkroute!(routes::person_follow))
.mount("/p/:npub/unfollow", mkroute!(routes::person_unfollow))
.mount("/p/:npub/send_dm", routes::person_send_dm)
.mount("/p/:npub/conversation", routes::person_dm_conversation)
.mount("/note/:event_id/thread", mkroute!(routes::note_thread))
.mount("/note/:event_id/reply", mkroute!(routes::note_reply))
.mount("/note/:event_id/repost", mkroute!(routes::note_repost))
.mount("/note/:event_id/react", mkroute!(routes::note_react))
.mount("/draft/new", mkroute!(routes::draft_new))
.mount("/draft/:draft_id/edit", mkroute!(routes::draft_edit))
.mount("/draft/:draft_id/post", mkroute!(routes::draft_post))
.mount("/inbox", mkroute!(routes::inbox))
.mount("/nsite/ls", mkroute!(routes::nsites_index))
.mount(
"/draft/:draft_id/upload_file",
mkroute!(routes::draft_upload_file),
)
.mount("/polls", mkroute!(routes::polls_list))
.mount(
"/polls/:event_id/respond/:option_id",
mkroute!(routes::poll_respond),
)
.mount("/polls/:event_id", mkroute!(routes::poll_details))
.add_footer(|_context: windmark_titanesque::context::RouteContext| {
format!("caracal version {}", env!("CARGO_PKG_VERSION"))
})
.set_error_handler(|_| Response::permanent_failure("Error!"))
.run()
.await
}