2024-04-09 01:14:48 +02:00
|
|
|
pub mod server;
|
|
|
|
pub mod model;
|
|
|
|
pub mod routes;
|
2024-04-06 18:03:13 +02:00
|
|
|
|
2024-04-09 01:14:48 +02:00
|
|
|
pub mod errors;
|
2024-04-06 18:03:13 +02:00
|
|
|
|
|
|
|
#[cfg(feature = "migrations")]
|
|
|
|
mod migrations;
|
|
|
|
|
|
|
|
#[cfg(feature = "migrations")]
|
|
|
|
use sea_orm_migration::MigratorTrait;
|
|
|
|
|
2024-03-16 03:29:06 +01:00
|
|
|
use clap::{Parser, Subcommand};
|
2024-03-19 07:39:12 +01:00
|
|
|
use sea_orm::{ConnectOptions, Database, EntityTrait, IntoActiveModel};
|
2024-03-16 03:29:06 +01:00
|
|
|
|
2024-03-28 04:52:17 +01:00
|
|
|
pub use errors::UpubResult as Result;
|
2024-04-13 22:31:46 +02:00
|
|
|
use tower_http::{cors::CorsLayer, trace::TraceLayer};
|
2024-03-28 04:52:17 +01:00
|
|
|
|
2024-04-13 22:13:36 +02:00
|
|
|
use crate::server::fetcher::Fetchable;
|
|
|
|
|
2024-03-25 05:02:20 +01:00
|
|
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
|
|
|
2024-03-16 03:29:06 +01:00
|
|
|
#[derive(Parser)]
|
|
|
|
/// all names were taken
|
|
|
|
struct CliArgs {
|
|
|
|
#[clap(subcommand)]
|
|
|
|
/// command to run
|
|
|
|
command: CliCommand,
|
|
|
|
|
2024-03-20 09:04:08 +01:00
|
|
|
#[arg(short = 'd', long = "db", default_value = "sqlite://./upub.db")]
|
2024-03-16 03:29:06 +01:00
|
|
|
/// database connection uri
|
|
|
|
database: String,
|
|
|
|
|
2024-03-20 09:04:08 +01:00
|
|
|
#[arg(short = 'D', long, default_value = "http://localhost:3000")]
|
2024-03-20 08:56:35 +01:00
|
|
|
/// instance base domain, for AP ids
|
|
|
|
domain: String,
|
|
|
|
|
2024-03-16 03:29:06 +01:00
|
|
|
#[arg(long, default_value_t=false)]
|
|
|
|
/// run with debug level tracing
|
|
|
|
debug: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Subcommand)]
|
|
|
|
enum CliCommand {
|
|
|
|
/// run fediverse server
|
|
|
|
Serve ,
|
|
|
|
|
2024-04-06 18:03:13 +02:00
|
|
|
#[cfg(feature = "migrations")]
|
2024-03-16 03:29:06 +01:00
|
|
|
/// apply database migrations
|
|
|
|
Migrate,
|
2024-03-16 05:46:14 +01:00
|
|
|
|
2024-04-06 18:03:13 +02:00
|
|
|
#[cfg(feature = "faker")]
|
2024-03-16 05:46:14 +01:00
|
|
|
/// generate fake user, note and activity
|
2024-03-26 00:53:07 +01:00
|
|
|
Faker{
|
|
|
|
/// how many fake statuses to insert for root user
|
2024-03-26 00:58:01 +01:00
|
|
|
count: u64,
|
2024-03-26 00:53:07 +01:00
|
|
|
},
|
2024-03-19 07:39:12 +01:00
|
|
|
|
|
|
|
/// fetch a single AP object
|
|
|
|
Fetch {
|
|
|
|
/// object id, or uri, to fetch
|
|
|
|
uri: String,
|
|
|
|
|
|
|
|
#[arg(long, default_value_t = false)]
|
|
|
|
/// store fetched object in local db
|
|
|
|
save: bool,
|
|
|
|
},
|
2024-03-16 03:29:06 +01:00
|
|
|
}
|
2024-02-09 17:07:55 +01:00
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
async fn main() {
|
2024-03-15 19:43:29 +01:00
|
|
|
|
2024-03-16 03:29:06 +01:00
|
|
|
let args = CliArgs::parse();
|
|
|
|
|
|
|
|
tracing_subscriber::fmt()
|
|
|
|
.compact()
|
|
|
|
.with_max_level(if args.debug { tracing::Level::DEBUG } else { tracing::Level::INFO })
|
|
|
|
.init();
|
|
|
|
|
2024-03-22 17:54:49 +01:00
|
|
|
// TODO can i do connectoptions.into() or .connect() and skip these ugly bindings?
|
2024-03-16 05:45:58 +01:00
|
|
|
let mut opts = ConnectOptions::new(&args.database);
|
2024-03-22 17:54:49 +01:00
|
|
|
|
2024-03-16 05:45:58 +01:00
|
|
|
opts
|
2024-03-22 17:54:49 +01:00
|
|
|
.sqlx_logging_level(tracing::log::LevelFilter::Debug);
|
2024-03-16 05:45:58 +01:00
|
|
|
|
|
|
|
let db = Database::connect(opts)
|
2024-03-16 03:29:06 +01:00
|
|
|
.await.expect("error connecting to db");
|
|
|
|
|
|
|
|
match args.command {
|
2024-04-06 18:03:13 +02:00
|
|
|
#[cfg(feature = "migrations")]
|
2024-03-16 03:29:06 +01:00
|
|
|
CliCommand::Migrate => migrations::Migrator::up(&db, None)
|
|
|
|
.await.expect("error applying migrations"),
|
2024-03-16 05:46:14 +01:00
|
|
|
|
2024-04-06 18:03:13 +02:00
|
|
|
#[cfg(feature = "faker")]
|
2024-03-26 00:58:01 +01:00
|
|
|
CliCommand::Faker { count } => model::faker::faker(&db, args.domain, count)
|
2024-03-16 05:46:14 +01:00
|
|
|
.await.expect("error creating fake entities"),
|
2024-03-19 07:39:12 +01:00
|
|
|
|
2024-04-13 22:13:36 +02:00
|
|
|
CliCommand::Fetch { uri, save } => fetch(db, args.domain, uri, save)
|
2024-03-19 07:39:12 +01:00
|
|
|
.await.expect("error fetching object"),
|
2024-04-09 04:35:57 +02:00
|
|
|
|
|
|
|
CliCommand::Serve => {
|
|
|
|
let ctx = server::Context::new(db, args.domain)
|
|
|
|
.await.expect("failed creating server context");
|
|
|
|
|
2024-04-11 00:29:32 +02:00
|
|
|
use routes::activitypub::ActivityPubRouter;
|
|
|
|
use routes::mastodon::MastodonRouter;
|
|
|
|
|
|
|
|
let router = axum::Router::new()
|
|
|
|
.ap_routes()
|
|
|
|
.mastodon_routes() // no-op if mastodon feature is disabled
|
2024-04-13 22:31:46 +02:00
|
|
|
.layer(CorsLayer::permissive())
|
|
|
|
.layer(TraceLayer::new_for_http())
|
2024-04-11 00:29:32 +02:00
|
|
|
.with_state(ctx);
|
2024-04-09 04:35:57 +02:00
|
|
|
|
|
|
|
// run our app with hyper, listening locally on port 3000
|
|
|
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
|
|
|
|
.await.expect("could not bind tcp socket");
|
|
|
|
|
|
|
|
axum::serve(listener, router)
|
|
|
|
.await
|
|
|
|
.expect("failed serving application")
|
|
|
|
},
|
2024-03-15 19:43:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-04-13 22:13:36 +02:00
|
|
|
async fn fetch(db: sea_orm::DatabaseConnection, domain: String, uri: String, save: bool) -> crate::Result<()> {
|
2024-04-13 22:17:35 +02:00
|
|
|
use apb::Base;
|
2024-03-19 07:39:12 +01:00
|
|
|
|
2024-04-13 22:13:36 +02:00
|
|
|
let ctx = server::Context::new(db, domain)
|
|
|
|
.await.expect("failed creating server context");
|
|
|
|
|
|
|
|
let mut node = apb::Node::link(uri.to_string());
|
|
|
|
node.fetch(&ctx).await?;
|
2024-03-19 07:39:12 +01:00
|
|
|
|
|
|
|
let obj = node.get().expect("node still empty after fetch?");
|
|
|
|
|
|
|
|
if save {
|
|
|
|
match obj.base_type() {
|
2024-04-06 16:56:13 +02:00
|
|
|
Some(apb::BaseType::Object(apb::ObjectType::Actor(_))) => {
|
2024-03-19 07:39:12 +01:00
|
|
|
model::user::Entity::insert(
|
2024-04-06 16:56:13 +02:00
|
|
|
model::user::Model::new(obj).unwrap().into_active_model()
|
2024-04-13 22:13:36 +02:00
|
|
|
).exec(ctx.db()).await.unwrap();
|
2024-03-19 07:39:12 +01:00
|
|
|
},
|
2024-04-06 16:56:13 +02:00
|
|
|
Some(apb::BaseType::Object(apb::ObjectType::Activity(_))) => {
|
2024-03-19 07:39:12 +01:00
|
|
|
model::activity::Entity::insert(
|
2024-04-06 16:56:13 +02:00
|
|
|
model::activity::Model::new(obj).unwrap().into_active_model()
|
2024-04-13 22:13:36 +02:00
|
|
|
).exec(ctx.db()).await.unwrap();
|
2024-03-19 07:39:12 +01:00
|
|
|
},
|
2024-04-06 16:56:13 +02:00
|
|
|
Some(apb::BaseType::Object(apb::ObjectType::Note)) => {
|
2024-03-19 07:39:12 +01:00
|
|
|
model::object::Entity::insert(
|
2024-04-06 16:56:13 +02:00
|
|
|
model::object::Model::new(obj).unwrap().into_active_model()
|
2024-04-13 22:13:36 +02:00
|
|
|
).exec(ctx.db()).await.unwrap();
|
2024-03-19 07:39:12 +01:00
|
|
|
},
|
2024-04-06 16:56:13 +02:00
|
|
|
Some(apb::BaseType::Object(t)) => tracing::warn!("not implemented: {:?}", t),
|
|
|
|
Some(apb::BaseType::Link(_)) => tracing::error!("fetched another link?"),
|
2024-03-19 07:39:12 +01:00
|
|
|
None => tracing::error!("no type on object"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-13 22:17:35 +02:00
|
|
|
println!("{}", serde_json::to_string_pretty(&obj).unwrap());
|
|
|
|
|
2024-03-19 07:39:12 +01:00
|
|
|
Ok(())
|
|
|
|
}
|