mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 07:21:10 +00:00
parent
706713be9d
commit
fdda0f584a
28 changed files with 1689 additions and 1330 deletions
|
@ -1,5 +1,5 @@
|
|||
variables:
|
||||
- &rust_image "rust:1.75"
|
||||
- &rust_image "rust:1.81"
|
||||
- &install_binstall "wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz && tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz && cp cargo-binstall /usr/local/cargo/bin"
|
||||
|
||||
steps:
|
||||
|
@ -11,6 +11,8 @@ steps:
|
|||
commands:
|
||||
- rustup component add rustfmt
|
||||
- cargo +nightly fmt -- --check
|
||||
when:
|
||||
- event: pull_request
|
||||
|
||||
leptos_fmt:
|
||||
image: *rust_image
|
||||
|
|
1537
Cargo.lock
generated
1537
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
67
Cargo.toml
67
Cargo.toml
|
@ -26,60 +26,61 @@ dbg_macro = "deny"
|
|||
unwrap_used = "deny"
|
||||
|
||||
[dependencies]
|
||||
activitypub_federation = { version = "0.5.2", features = [
|
||||
activitypub_federation = { version = "0.6.0-alpha2", features = [
|
||||
"axum",
|
||||
"diesel",
|
||||
], default-features = false, optional = true }
|
||||
anyhow = "1.0.75"
|
||||
async-trait = "0.1.74"
|
||||
axum = { version = "0.6.20", optional = true }
|
||||
axum-macros = { version = "0.3.8", optional = true }
|
||||
axum-extra = { version = "0.7.7", features = ["cookie"], optional = true }
|
||||
leptos = "0.5.4"
|
||||
leptos_meta = "0.5.4"
|
||||
leptos_router = "0.5.4"
|
||||
leptos_axum = { version = "0.5.4", optional = true }
|
||||
bcrypt = "0.15.0"
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
diesel = { version = "2.1.4", features = [
|
||||
anyhow = "1.0.89"
|
||||
async-trait = "0.1.83"
|
||||
axum = { version = "0.7.7", optional = true }
|
||||
axum-macros = { version = "0.4.2", optional = true }
|
||||
axum-extra = { version = "0.9.4", features = ["cookie"], optional = true }
|
||||
leptos = "0.6.15"
|
||||
leptos_meta = "0.6.15"
|
||||
leptos_router = "0.6.15"
|
||||
leptos_axum = { version = "0.6.15", optional = true }
|
||||
bcrypt = "0.15.1"
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
diesel = { version = "2.2.4", features = [
|
||||
"postgres",
|
||||
"chrono",
|
||||
"uuid",
|
||||
"r2d2",
|
||||
], optional = true }
|
||||
diesel-derive-newtype = { version = "2.1.0", optional = true }
|
||||
diesel_migrations = { version = "2.1.0", optional = true }
|
||||
diffy = "0.3.0"
|
||||
diesel-derive-newtype = { version = "2.1.2", optional = true }
|
||||
diesel_migrations = { version = "2.2.0", optional = true }
|
||||
diffy = "0.4.0"
|
||||
enum_delegate = "0.2.0"
|
||||
env_logger = { version = "0.10.1", default-features = false }
|
||||
futures = "0.3.29"
|
||||
env_logger = { version = "0.11.5", default-features = false }
|
||||
futures = "0.3.30"
|
||||
hex = "0.4.3"
|
||||
jsonwebtoken = { version = "9.2.0", optional = true }
|
||||
jsonwebtoken = { version = "9.3.0", optional = true }
|
||||
rand = "0.8.5"
|
||||
serde_json = "1.0.108"
|
||||
serde_json = "1.0.128"
|
||||
sha2 = "0.10.8"
|
||||
tokio = { version = "1.34.0", features = ["full"], optional = true }
|
||||
uuid = { version = "1.6.1", features = ["serde"] }
|
||||
tower-http = { version = "0.4.0", features = ["cors", "fs"], optional = true }
|
||||
serde = { version = "1.0.192", features = ["derive"] }
|
||||
url = { version = "2.4.1", features = ["serde"] }
|
||||
reqwest = { version = "0.11.22", features = ["json", "cookies"] }
|
||||
tokio = { version = "1.40.0", features = ["full"], optional = true }
|
||||
uuid = { version = "1.10.0", features = ["serde"] }
|
||||
tower-http = { version = "0.6.1", features = ["cors", "fs"], optional = true }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
url = { version = "2.5.2", features = ["serde"] }
|
||||
reqwest = { version = "0.12.8", features = ["json", "cookies"] }
|
||||
log = "0.4"
|
||||
tracing = "0.1.40"
|
||||
once_cell = "1.18.0"
|
||||
wasm-bindgen = "0.2.89"
|
||||
once_cell = "1.20.1"
|
||||
wasm-bindgen = "0.2.93"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1.0.0"
|
||||
time = "0.3.31"
|
||||
tower = "0.4.13"
|
||||
markdown-it = "0.6.0"
|
||||
web-sys = "0.3.68"
|
||||
time = "0.3.36"
|
||||
tower = "0.5.1"
|
||||
markdown-it = "0.6.1"
|
||||
web-sys = "0.3.70"
|
||||
config = { version = "0.14.0", features = ["toml"] }
|
||||
doku = "0.21.1"
|
||||
smart-default = "0.7.1"
|
||||
tower-layer = "0.3.3"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
pretty_assertions = "1.4.1"
|
||||
|
||||
[package.metadata.leptos]
|
||||
output-name = "ibis"
|
||||
|
|
|
@ -9,5 +9,5 @@ IBIS__BIND="${IBIS_BIND:-"127.0.0.1:8081"}"
|
|||
# start frontend
|
||||
CARGO_TARGET_DIR=target/frontend trunk serve -w src/frontend/ --proxy-backend http://$IBIS__BIND &
|
||||
# start backend, with separate target folder to avoid rebuilds from arch change
|
||||
cargo watch -x run
|
||||
bacon -j run
|
||||
)
|
||||
|
|
|
@ -29,6 +29,7 @@ use crate::{
|
|||
};
|
||||
use activitypub_federation::config::Data;
|
||||
use axum::{
|
||||
body::Body,
|
||||
http::{Request, StatusCode},
|
||||
middleware::{self, Next},
|
||||
response::Response,
|
||||
|
@ -45,7 +46,7 @@ pub mod article;
|
|||
pub mod instance;
|
||||
pub mod user;
|
||||
|
||||
pub fn api_routes() -> Router {
|
||||
pub fn api_routes() -> Router<()> {
|
||||
Router::new()
|
||||
.route(
|
||||
"/article",
|
||||
|
@ -68,11 +69,11 @@ pub fn api_routes() -> Router {
|
|||
.route_layer(middleware::from_fn(auth))
|
||||
}
|
||||
|
||||
async fn auth<B>(
|
||||
async fn auth(
|
||||
data: Data<IbisData>,
|
||||
jar: CookieJar,
|
||||
mut request: Request<B>,
|
||||
next: Next<B>,
|
||||
mut request: Request<Body>,
|
||||
next: Next,
|
||||
) -> Result<Response, StatusCode> {
|
||||
if let Some(auth) = jar.get(AUTH_COOKIE) {
|
||||
if let Ok(user) = validate(auth.value(), &data).await {
|
||||
|
|
|
@ -98,7 +98,7 @@ fn create_cookie(jwt: String, data: &Data<IbisData>) -> Cookie<'static> {
|
|||
if domain.contains(':') {
|
||||
domain = domain.split(':').collect::<Vec<_>>()[0].to_string();
|
||||
}
|
||||
Cookie::build(AUTH_COOKIE, jwt)
|
||||
Cookie::build((AUTH_COOKIE, jwt))
|
||||
.domain(domain)
|
||||
.same_site(SameSite::Strict)
|
||||
.path("/")
|
||||
|
@ -107,7 +107,7 @@ fn create_cookie(jwt: String, data: &Data<IbisData>) -> Cookie<'static> {
|
|||
.expires(Expiration::DateTime(
|
||||
OffsetDateTime::now_utc() + Duration::weeks(52),
|
||||
))
|
||||
.finish()
|
||||
.build()
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
|
|
|
@ -42,7 +42,7 @@ use chrono::{DateTime, Utc};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
pub fn federation_routes() -> Router {
|
||||
pub fn federation_routes() -> Router<()> {
|
||||
Router::new()
|
||||
.route("/", get(http_get_instance))
|
||||
.route("/user/:name", get(http_get_person))
|
||||
|
|
|
@ -23,27 +23,28 @@ use activitypub_federation::{
|
|||
};
|
||||
use api::api_routes;
|
||||
use axum::{
|
||||
debug_handler,
|
||||
headers::HeaderMap,
|
||||
body::Body,
|
||||
http::{HeaderValue, Request},
|
||||
middleware::Next,
|
||||
response::{IntoResponse, Response},
|
||||
routing::get,
|
||||
Router,
|
||||
Server,
|
||||
ServiceExt,
|
||||
};
|
||||
use axum_macros::{debug_handler, debug_middleware};
|
||||
use chrono::Local;
|
||||
use diesel::{
|
||||
r2d2::{ConnectionManager, Pool},
|
||||
PgConnection,
|
||||
};
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
use leptos::{leptos_config::get_config_from_str, *};
|
||||
use leptos::leptos_config::get_config_from_str;
|
||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||
use log::info;
|
||||
use tower::Layer;
|
||||
use reqwest::header::HeaderMap;
|
||||
use tokio::net::TcpListener;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use tower_layer::Layer;
|
||||
|
||||
pub mod api;
|
||||
pub mod config;
|
||||
|
@ -81,15 +82,14 @@ pub async fn start(config: IbisConfig) -> MyResult<()> {
|
|||
setup(&data.to_request_data()).await?;
|
||||
}
|
||||
|
||||
let conf = get_config_from_str(include_str!("../../Cargo.toml"))?;
|
||||
let mut leptos_options = conf.leptos_options;
|
||||
leptos_options.site_addr = data.config.bind;
|
||||
let mut conf = get_config_from_str(include_str!("../../Cargo.toml"))?;
|
||||
conf.site_addr = data.config.bind;
|
||||
let routes = generate_route_list(App);
|
||||
|
||||
let config = data.clone();
|
||||
let app = Router::new()
|
||||
.leptos_routes(&leptos_options, routes, || view! { <App/> })
|
||||
.with_state(leptos_options)
|
||||
.leptos_routes(&conf, routes, App)
|
||||
.with_state(conf)
|
||||
.nest("", asset_routes()?)
|
||||
.nest(FEDERATION_ROUTES_PREFIX, federation_routes())
|
||||
.nest("/api/v1", api_routes())
|
||||
|
@ -103,14 +103,13 @@ pub async fn start(config: IbisConfig) -> MyResult<()> {
|
|||
let app_with_middleware = middleware.layer(app);
|
||||
|
||||
info!("Listening on {}", &data.config.bind);
|
||||
Server::bind(&data.config.bind)
|
||||
.serve(app_with_middleware.into_make_service())
|
||||
.await?;
|
||||
let listener = TcpListener::bind(&data.config.bind).await?;
|
||||
axum::serve(listener, app_with_middleware.into_make_service()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn asset_routes() -> MyResult<Router> {
|
||||
pub fn asset_routes() -> MyResult<Router<()>> {
|
||||
let mut css_headers = HeaderMap::new();
|
||||
css_headers.insert("Content-Type", "text/css".parse()?);
|
||||
Ok(Router::new()
|
||||
|
@ -209,7 +208,8 @@ async fn setup(data: &Data<IbisData>) -> Result<(), Error> {
|
|||
/// with frontend routes. If a request is an Activitypub fetch as indicated by
|
||||
/// `Accept: application/activity+json` header, use the federation routes. Otherwise
|
||||
/// leave the path unchanged so it can go to frontend.
|
||||
async fn federation_routes_middleware<B>(request: Request<B>, next: Next<B>) -> Response {
|
||||
#[debug_middleware]
|
||||
async fn federation_routes_middleware(request: Request<Body>, next: Next) -> Response {
|
||||
let (mut parts, body) = request.into_parts();
|
||||
// rewrite uri based on accept header
|
||||
let mut uri = parts.uri.to_string();
|
||||
|
|
|
@ -7,7 +7,7 @@ use axum::{routing::get, Json, Router};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
pub fn config() -> Router {
|
||||
pub fn config() -> Router<()> {
|
||||
Router::new()
|
||||
.route("/nodeinfo/2.0.json", get(node_info))
|
||||
.route("/.well-known/nodeinfo", get(node_info_well_known))
|
||||
|
|
|
@ -48,12 +48,16 @@ pub fn ArticleNav(article: Resource<Option<String>, ArticleView>) -> impl IntoVi
|
|||
}>
|
||||
<A href=format!("{article_link}/edit")>"Edit"</A>
|
||||
</Show>
|
||||
<Show when=move || global_state.with(|state| state.my_profile.is_some())>
|
||||
<Show when=move || {
|
||||
global_state.with(|state| state.my_profile.is_some())
|
||||
}>
|
||||
<A href=format!("{article_link_}/actions")>"Actions"</A>
|
||||
{instance
|
||||
.get()
|
||||
.map(|i| {
|
||||
view! { <InstanceFollowButton instance=i.instance.clone()/> }
|
||||
view! {
|
||||
<InstanceFollowButton instance=i.instance.clone() />
|
||||
}
|
||||
})}
|
||||
|
||||
</Show>
|
||||
|
|
|
@ -63,8 +63,7 @@ pub fn CreateArticle() -> impl IntoView {
|
|||
let val = event_target_value(&ev);
|
||||
set_text.update(|p| *p = val);
|
||||
}
|
||||
>
|
||||
</textarea>
|
||||
></textarea>
|
||||
<div>
|
||||
<a href="https://commonmark.org/help/" target="blank_">
|
||||
Markdown
|
||||
|
@ -90,7 +89,9 @@ pub fn CreateArticle() -> impl IntoView {
|
|||
|
||||
<button
|
||||
prop:disabled=move || button_is_disabled.get()
|
||||
on:click=move |_| submit_action.dispatch((title.get(), text.get(), summary.get()))
|
||||
on:click=move |_| {
|
||||
submit_action.dispatch((title.get(), text.get(), summary.get()))
|
||||
}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
|
|
|
@ -25,7 +25,9 @@ pub fn ReadArticle() -> impl IntoView {
|
|||
view! {
|
||||
<div class="item-view">
|
||||
<h1>{article_title(&article.article)}</h1>
|
||||
<div inner_html=parser.parse(&article.article.text).render()></div>
|
||||
<div inner_html=parser
|
||||
.parse(&article.article.text)
|
||||
.render()></div>
|
||||
</div>
|
||||
}
|
||||
})
|
||||
|
|
|
@ -18,10 +18,16 @@ pub fn Conflicts() -> impl IntoView {
|
|||
.map(|c| {
|
||||
c.into_iter()
|
||||
.map(|c| {
|
||||
let link = format!("{}/edit/{}", article_link(&c.article), c.id);
|
||||
let link = format!(
|
||||
"{}/edit/{}",
|
||||
article_link(&c.article),
|
||||
c.id,
|
||||
);
|
||||
view! {
|
||||
<li>
|
||||
<a href=link>{article_title(&c.article)} " - " {c.summary}</a>
|
||||
<a href=link>
|
||||
{article_title(&c.article)} " - " {c.summary}
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
})
|
||||
|
|
|
@ -31,10 +31,14 @@ pub fn InstanceDetails() -> impl IntoView {
|
|||
view! {
|
||||
<h1>{instance.domain}</h1>
|
||||
|
||||
<Show when=move || global_state.with(|state| state.my_profile.is_some())>
|
||||
<Show when=move || {
|
||||
global_state.with(|state| state.my_profile.is_some())
|
||||
}>
|
||||
<InstanceFollowButton instance=instance_.clone() />
|
||||
</Show>
|
||||
<p>Follow the instance so that new edits are federated to your instance.</p>
|
||||
<p>
|
||||
Follow the instance so that new edits are federated to your instance.
|
||||
</p>
|
||||
<p>
|
||||
"TODO: show a list of articles from the instance. For now you can use the "
|
||||
<a href="/article/list">Article list</a>.
|
||||
|
|
|
@ -93,8 +93,7 @@ pub fn Search() -> impl IntoView {
|
|||
]
|
||||
} else {
|
||||
vec![]
|
||||
}}
|
||||
// render articles from resolve/search
|
||||
}} // render articles from resolve/search
|
||||
{search_results
|
||||
.articles
|
||||
.iter()
|
||||
|
|
Loading…
Reference in a new issue