mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 08:21:09 +00:00
add basic instance details page (untested)
This commit is contained in:
parent
862297638f
commit
f2180e5e0b
16 changed files with 91 additions and 52 deletions
|
@ -1,6 +1,7 @@
|
|||
create table instance (
|
||||
id serial primary key,
|
||||
ap_id varchar(255) not null unique,
|
||||
description text,
|
||||
inbox_url text not null,
|
||||
articles_url varchar(255) not null unique,
|
||||
public_key text not null,
|
||||
|
@ -23,7 +24,8 @@ create table person (
|
|||
create table local_user (
|
||||
id serial primary key,
|
||||
password_encrypted text not null,
|
||||
person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL
|
||||
person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||
admin bool not null
|
||||
);
|
||||
|
||||
create table instance_follow (
|
||||
|
|
|
@ -58,7 +58,8 @@ pub(in crate::backend::api) async fn register_user(
|
|||
jar: CookieJar,
|
||||
Form(form): Form<RegisterUserData>,
|
||||
) -> MyResult<(CookieJar, Json<LocalUserView>)> {
|
||||
let user = DbPerson::create_local(form.username, form.password, &data)?;
|
||||
// TODO: make admin if its the first user account
|
||||
let user = DbPerson::create_local(form.username, form.password, false, &data)?;
|
||||
let token = generate_login_token(&user.local_user, &data)?;
|
||||
let jar = jar.add(create_cookie(token, &data));
|
||||
Ok((jar, Json(user)))
|
||||
|
|
|
@ -19,6 +19,7 @@ use std::sync::Mutex;
|
|||
#[diesel(table_name = instance, check_for_backend(diesel::pg::Pg))]
|
||||
pub struct DbInstanceForm {
|
||||
pub ap_id: ObjectId<DbInstance>,
|
||||
pub description: Option<String>,
|
||||
pub articles_url: CollectionId<DbArticleCollection>,
|
||||
pub inbox_url: String,
|
||||
pub public_key: String,
|
||||
|
|
|
@ -43,6 +43,7 @@ diesel::table! {
|
|||
id -> Int4,
|
||||
#[max_length = 255]
|
||||
ap_id -> Varchar,
|
||||
description -> Nullable<Text>,
|
||||
inbox_url -> Text,
|
||||
#[max_length = 255]
|
||||
articles_url -> Varchar,
|
||||
|
@ -74,6 +75,7 @@ diesel::table! {
|
|||
id -> Int4,
|
||||
password_encrypted -> Text,
|
||||
person_id -> Int4,
|
||||
admin -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ use std::sync::Mutex;
|
|||
pub struct DbLocalUserForm {
|
||||
pub password_encrypted: String,
|
||||
pub person_id: i32,
|
||||
pub admin: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
||||
|
@ -52,6 +53,7 @@ impl DbPerson {
|
|||
pub fn create_local(
|
||||
username: String,
|
||||
password: String,
|
||||
admin: bool,
|
||||
data: &Data<MyDataHandle>,
|
||||
) -> MyResult<LocalUserView> {
|
||||
let mut conn = data.db_connection.lock().unwrap();
|
||||
|
@ -76,6 +78,7 @@ impl DbPerson {
|
|||
let local_user_form = DbLocalUserForm {
|
||||
password_encrypted: hash(password, DEFAULT_COST)?,
|
||||
person_id: person.id,
|
||||
admin,
|
||||
};
|
||||
|
||||
let local_user = insert_into(local_user::table)
|
||||
|
|
|
@ -27,6 +27,7 @@ pub struct ApubInstance {
|
|||
#[serde(rename = "type")]
|
||||
kind: ServiceType,
|
||||
id: ObjectId<DbInstance>,
|
||||
content: Option<String>,
|
||||
articles: CollectionId<DbArticleCollection>,
|
||||
inbox: Url,
|
||||
public_key: PublicKey,
|
||||
|
@ -90,6 +91,7 @@ impl Object for DbInstance {
|
|||
Ok(ApubInstance {
|
||||
kind: Default::default(),
|
||||
id: self.ap_id.clone(),
|
||||
content: self.description.clone(),
|
||||
articles: self.articles_url.clone(),
|
||||
inbox: Url::parse(&self.inbox_url)?,
|
||||
public_key: self.public_key(),
|
||||
|
@ -108,6 +110,7 @@ impl Object for DbInstance {
|
|||
async fn from_json(json: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, Self::Error> {
|
||||
let form = DbInstanceForm {
|
||||
ap_id: json.id,
|
||||
description: json.content,
|
||||
articles_url: json.articles,
|
||||
inbox_url: json.inbox.to_string(),
|
||||
public_key: json.public_key.public_key_pem,
|
||||
|
|
|
@ -65,6 +65,7 @@ pub async fn start(hostname: &str, database_url: &str) -> MyResult<()> {
|
|||
let keypair = generate_actor_keypair()?;
|
||||
let form = DbInstanceForm {
|
||||
ap_id,
|
||||
description: Some("New Ibis instance".to_string()),
|
||||
articles_url,
|
||||
inbox_url,
|
||||
public_key: keypair.public_key,
|
||||
|
|
|
@ -133,6 +133,7 @@ pub struct DbLocalUser {
|
|||
pub id: i32,
|
||||
pub password_encrypted: String,
|
||||
pub person_id: i32,
|
||||
pub admin: bool,
|
||||
}
|
||||
|
||||
/// Federation related data from a local or remote user.
|
||||
|
@ -219,6 +220,7 @@ pub struct DbInstance {
|
|||
pub ap_id: ObjectId<DbInstance>,
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub ap_id: String,
|
||||
pub description: Option<String>,
|
||||
#[cfg(feature = "ssr")]
|
||||
pub articles_url: CollectionId<DbArticleCollection>,
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
|
|
|
@ -7,13 +7,13 @@ use crate::frontend::pages::article::history::ArticleHistory;
|
|||
use crate::frontend::pages::article::list::ListArticles;
|
||||
use crate::frontend::pages::article::read::ReadArticle;
|
||||
use crate::frontend::pages::diff::EditDiff;
|
||||
use crate::frontend::pages::instance_details::InstanceDetails;
|
||||
use crate::frontend::pages::login::Login;
|
||||
use crate::frontend::pages::register::Register;
|
||||
use crate::frontend::pages::search::Search;
|
||||
use crate::frontend::pages::Page;
|
||||
use leptos::{
|
||||
component, create_local_resource, create_rw_signal, expect_context, provide_context,
|
||||
use_context, view, IntoView, RwSignal, SignalGetUntracked, SignalUpdate,
|
||||
use_context, view, IntoView, RwSignal, SignalGet, SignalGetUntracked, SignalUpdate,
|
||||
};
|
||||
use leptos_meta::provide_meta_context;
|
||||
use leptos_meta::*;
|
||||
|
@ -47,6 +47,17 @@ impl GlobalState {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn is_admin() -> fn() -> bool {
|
||||
move || {
|
||||
use_context::<RwSignal<GlobalState>>()
|
||||
.expect("global state is provided")
|
||||
.get()
|
||||
.my_profile
|
||||
.map(|p| p.local_user.admin)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
@ -71,15 +82,16 @@ pub fn App() -> impl IntoView {
|
|||
<Nav />
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path={Page::Home.path()} view=ReadArticle/>
|
||||
<Route path="/" view=ReadArticle/>
|
||||
<Route path="/article/:title" view=ReadArticle/>
|
||||
<Route path="/article/:title/edit" view=EditArticle/>
|
||||
<Route path="/article/:title/history" view=ArticleHistory/>
|
||||
<Route path="/article/:title/diff/:hash" view=EditDiff/>
|
||||
<Route path="/article/create" view=CreateArticle/>
|
||||
<Route path="/article/list" view=ListArticles/>
|
||||
<Route path={Page::Login.path()} view=Login/>
|
||||
<Route path={Page::Register.path()} view=Register/>
|
||||
<Route path="/instance/:hostname" view=InstanceDetails/>
|
||||
<Route path="/login" view=Login/>
|
||||
<Route path="/register" view=Register/>
|
||||
<Route path="/search" view=Search/>
|
||||
</Routes>
|
||||
</main>
|
||||
|
|
|
@ -4,7 +4,7 @@ use leptos::*;
|
|||
use leptos_router::*;
|
||||
|
||||
#[component]
|
||||
pub fn ArticleNav(article: Resource<String, ArticleView>) -> impl IntoView {
|
||||
pub fn ArticleNav(article: Resource<Option<String>, ArticleView>) -> impl IntoView {
|
||||
let global_state = use_context::<RwSignal<GlobalState>>().unwrap();
|
||||
view! {
|
||||
<Suspense>
|
||||
|
|
|
@ -8,8 +8,8 @@ use leptos_router::use_params_map;
|
|||
#[component]
|
||||
pub fn EditArticle() -> impl IntoView {
|
||||
let params = use_params_map();
|
||||
let title = params.get_untracked().get("title").cloned();
|
||||
let article = article_resource(title.unwrap());
|
||||
let title = move || params.get().get("title").cloned();
|
||||
let article = article_resource(title);
|
||||
|
||||
let (text, set_text) = create_signal(String::new());
|
||||
let (summary, set_summary) = create_signal(String::new());
|
||||
|
|
|
@ -6,8 +6,8 @@ use leptos_router::*;
|
|||
#[component]
|
||||
pub fn ArticleHistory() -> impl IntoView {
|
||||
let params = use_params_map();
|
||||
let title = params.get_untracked().get("title").cloned();
|
||||
let article = article_resource(title.unwrap());
|
||||
let title = move || params.get().get("title").cloned();
|
||||
let article = article_resource(title);
|
||||
|
||||
view! {
|
||||
<ArticleNav article=article/>
|
||||
|
|
|
@ -7,11 +7,7 @@ use markdown_it::MarkdownIt;
|
|||
#[component]
|
||||
pub fn ReadArticle() -> impl IntoView {
|
||||
let params = use_params_map();
|
||||
let title = params
|
||||
.get_untracked()
|
||||
.get("title")
|
||||
.cloned()
|
||||
.unwrap_or("Main_Page".to_string());
|
||||
let title = move || params.get().get("title").cloned();
|
||||
let article = article_resource(title);
|
||||
|
||||
view! {
|
||||
|
|
|
@ -6,7 +6,7 @@ use leptos_router::*;
|
|||
#[component]
|
||||
pub fn EditDiff() -> impl IntoView {
|
||||
let params = use_params_map();
|
||||
let title = params.get_untracked().get("title").cloned().unwrap();
|
||||
let title = move || params.get().get("title").cloned();
|
||||
let article = article_resource(title);
|
||||
|
||||
view! {
|
||||
|
|
33
src/frontend/pages/instance_details.rs
Normal file
33
src/frontend/pages/instance_details.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use crate::common::DbInstance;
|
||||
use crate::frontend::app::GlobalState;
|
||||
use leptos::*;
|
||||
use leptos_router::use_params_map;
|
||||
use url::Url;
|
||||
|
||||
#[component]
|
||||
pub fn InstanceDetails() -> impl IntoView {
|
||||
let params = use_params_map();
|
||||
let hostname = move || params.get().get("hostname").cloned().unwrap();
|
||||
let instance_profile = create_resource(hostname, move |hostname| async move {
|
||||
let url = Url::parse(&format!("http://{hostname}")).unwrap();
|
||||
GlobalState::api_client()
|
||||
.resolve_instance(url)
|
||||
.await
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
// TODO: display list of articles from instance?
|
||||
view! {
|
||||
<Suspense fallback=|| view! { "Loading..." }> {
|
||||
move || instance_profile.get().map(|instance: DbInstance| {
|
||||
view! {
|
||||
<h1>{instance.ap_id.to_string()}</h1>
|
||||
<Show when=GlobalState::is_admin()>
|
||||
<button text="Follow"/>
|
||||
</Show>
|
||||
<div>{instance.description}</div>
|
||||
}
|
||||
})
|
||||
}</Suspense>
|
||||
}
|
||||
}
|
|
@ -4,40 +4,23 @@ use leptos::{create_resource, Resource};
|
|||
|
||||
pub(crate) mod article;
|
||||
pub(crate) mod diff;
|
||||
pub mod login;
|
||||
pub mod register;
|
||||
pub(crate) mod instance_details;
|
||||
pub(crate) mod login;
|
||||
pub(crate) mod register;
|
||||
pub(crate) mod search;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub enum Page {
|
||||
#[default]
|
||||
Home,
|
||||
Login,
|
||||
Register,
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub fn path(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Home => "/",
|
||||
Self::Login => "/login",
|
||||
Self::Register => "/register",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn article_resource(title: String) -> Resource<String, ArticleView> {
|
||||
create_resource(
|
||||
move || title.clone(),
|
||||
move |title| async move {
|
||||
GlobalState::api_client()
|
||||
.get_article(GetArticleData {
|
||||
title: Some(title),
|
||||
instance_id: None,
|
||||
id: None,
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
},
|
||||
)
|
||||
fn article_resource(
|
||||
title: impl Fn() -> Option<String> + 'static,
|
||||
) -> Resource<Option<String>, ArticleView> {
|
||||
create_resource(title, move |title| async move {
|
||||
let title = title.unwrap_or("Main_Page".to_string());
|
||||
GlobalState::api_client()
|
||||
.get_article(GetArticleData {
|
||||
title: Some(title),
|
||||
instance_id: None,
|
||||
id: None,
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue