mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add federation webpage to society; fix dead link on home page
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3094,6 +3094,7 @@ dependencies = [
|
|||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"actix-ws",
|
"actix-ws",
|
||||||
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
|
|||||||
72
assets/templates/rc_society/federation.html.hbs
Normal file
72
assets/templates/rc_society/federation.html.hbs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<!-- Primary Meta Tags -->
|
||||||
|
<title>Federation - Openjam</title>
|
||||||
|
<meta name="title" content="Federation - Openjam" />
|
||||||
|
<meta name="description" content="View federation of this third-party Robocraft servers" />
|
||||||
|
|
||||||
|
<!-- Open Graph / Facebook -->
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="https://git.ngram.ca/OpenJam/rc-servers" />
|
||||||
|
<meta property="og:title" content="Federation - Openjam" />
|
||||||
|
<meta property="og:description" content="View federation of this third-party Robocraft servers" />
|
||||||
|
<meta property="og:image" content="https://git.ngram.ca/OpenJam/rc-servers/raw/branch/main/assets/robocraft/favicon.jpg" />
|
||||||
|
|
||||||
|
<!-- Twitter -->
|
||||||
|
<meta property="twitter:card" content="summary" />
|
||||||
|
<meta property="twitter:url" content="https://git.ngram.ca/OpenJam/rc-servers" />
|
||||||
|
<meta property="twitter:title" content="Federation - Openjam" />
|
||||||
|
<meta property="twitter:description" content="View federation of this third-party Robocraft servers" />
|
||||||
|
<meta property="twitter:image" content="https://git.ngram.ca/OpenJam/rc-servers/raw/branch/main/assets/robocraft/favicon.jpg" />
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
{{> stylesheet}}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="form-stripe">
|
||||||
|
<div class="form-header-right">
|
||||||
|
Welcome {{form.display_name}}! |
|
||||||
|
<a href="/">Home</a> |
|
||||||
|
<a href="/dashboard">Dashboard</a> |
|
||||||
|
<a href="/logout">Sign Out</a>
|
||||||
|
</div>
|
||||||
|
<h1 class="form-banner">Federation Details</h1>
|
||||||
|
{{#if error}}
|
||||||
|
<div class="form-error">
|
||||||
|
<p class="form-error-message">{{error}}</p>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="access-list">
|
||||||
|
{{#each form.instances}}
|
||||||
|
<div class="access-row-box">
|
||||||
|
<h4 class="access-box-title">{{this.domain}}</h4>
|
||||||
|
<div class="box-details">
|
||||||
|
<div class="box-details-key">Database ID</div>
|
||||||
|
<div class="box-details-value">{{this.id}}</div>
|
||||||
|
<div class="box-details-key">Auth Server</div>
|
||||||
|
<div class="box-details-value"><a class="box-details-link" href="{{this.auth}}">{{this.auth}}</a></div>
|
||||||
|
<div class="box-details-key">Society Server</div>
|
||||||
|
<div class="box-details-value"><a class="box-details-link" href="{{this.society}}">{{this.society}}</a></div>
|
||||||
|
<div class="box-details-key">First Seen</div>
|
||||||
|
<div class="box-details-value"><time datetime="{{this.first_used_iso}}">{{this.first_used_iso}}</time></div>
|
||||||
|
<div class="box-details-key">Last Seen</div>
|
||||||
|
<div class="box-details-value"><time datetime="{{this.first_used_iso}}">{{this.first_used_iso}}</time></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="footer">
|
||||||
|
{{version}}
|
||||||
|
| <a href="{{source_url}}#readme">About</a>
|
||||||
|
| <a href="https://liberapay.com/NGram">Support Me</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
<div class="form-text-block">
|
<div class="form-text-block">
|
||||||
<p>
|
<p>
|
||||||
This service allows you to manage your account settings, import and export your garage bays, view the server config, and will eventually (not yet) federate with other OpenJam rc-servers instances.
|
This service allows you to manage your account settings, import and export your garage bays, view the server config, and will eventually (not yet) federate with other OpenJam rc-servers instances.
|
||||||
You can view other known instances <a href="/federation">here</a>.
|
{{#if form.is_logged_in}}You can view other known instances <a href="/federation">here</a>.{{/if}}
|
||||||
This service is intended to have stable public APIs for expanding existing functionality.
|
This service is intended to have stable public APIs for expanding existing functionality.
|
||||||
If you do not want any of this functionality in your instance, simply do not run this service with your instance.
|
If you do not want any of this functionality in your instance, simply do not run this service with your instance.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ mod inventory;
|
|||||||
pub use inventory::{UnlockedParts, UnlockOverride};
|
pub use inventory::{UnlockedParts, UnlockOverride};
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserAuthInfo, FederatedAuthInfo, UserLoginInfo, UserAuthenticator, FederatedAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, PlayerScore, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser, IntercomListener, UserRole, SocialUser, SocialUserC, CurrencyType, CurrencyOp, MatchRewards, SingleplayerUser, PurchaseResult, FactoryUser, FriendInviteReturn, FriendData, FriendInviteStatus, SocialInfo, ClanData, ClanMember, ClanMemberRank, ClanType, ClanSearchQuery, ClanInviteData, Userless, GameOverrides, WebUser, GarageWebInfo, GarageWebStats, SanctionWebStats, AccountWebStats, SocialWebStats};
|
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserAuthInfo, FederatedAuthInfo, UserLoginInfo, UserAuthenticator, FederatedAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, PlayerScore, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser, IntercomListener, UserRole, SocialUser, SocialUserC, CurrencyType, CurrencyOp, MatchRewards, SingleplayerUser, PurchaseResult, FactoryUser, FriendInviteReturn, FriendData, FriendInviteStatus, SocialInfo, ClanData, ClanMember, ClanMemberRank, ClanType, ClanSearchQuery, ClanInviteData, Userless, GameOverrides, WebUser, GarageWebInfo, GarageWebStats, SanctionWebStats, AccountWebStats, SocialWebStats, FederationWebData, FederationWebDetails};
|
||||||
|
|
||||||
pub mod intercom;
|
pub mod intercom;
|
||||||
pub use intercom::generate_token as generate_intercom_token;
|
pub use intercom::generate_token as generate_intercom_token;
|
||||||
|
|||||||
@@ -703,6 +703,7 @@ pub trait WebUser: CommonUser {
|
|||||||
async fn account_stats(&self) -> Result<AccountWebStats, Box<dyn std::error::Error>>;
|
async fn account_stats(&self) -> Result<AccountWebStats, Box<dyn std::error::Error>>;
|
||||||
async fn sanction_stats(&self) -> Result<SanctionWebStats, Box<dyn std::error::Error>>;
|
async fn sanction_stats(&self) -> Result<SanctionWebStats, Box<dyn std::error::Error>>;
|
||||||
async fn social_stats(&self) -> Result<SocialWebStats, Box<dyn std::error::Error>>;
|
async fn social_stats(&self) -> Result<SocialWebStats, Box<dyn std::error::Error>>;
|
||||||
|
async fn fedi_info(&self) -> Result<FederationWebData, Box<dyn std::error::Error>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GarageWebInfo {
|
pub struct GarageWebInfo {
|
||||||
@@ -749,6 +750,19 @@ pub struct SocialWebStats {
|
|||||||
pub chats: Vec<String>,
|
pub chats: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FederationWebData {
|
||||||
|
pub federations: Vec<FederationWebDetails>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FederationWebDetails {
|
||||||
|
pub id: i32,
|
||||||
|
pub domain: String,
|
||||||
|
pub auth: String,
|
||||||
|
pub society: String,
|
||||||
|
pub last_used: i64,
|
||||||
|
pub first_used: i64,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait Userless: Send + Sync {
|
pub trait Userless: Send + Sync {
|
||||||
async fn lobby_state_listener(&self) -> Result<super::IntercomListener<super::intercom::IntercomLobbyStateMessage>, reqwest_websocket::Error>;
|
async fn lobby_state_listener(&self) -> Result<super::IntercomListener<super::intercom::IntercomLobbyStateMessage>, reqwest_websocket::Error>;
|
||||||
|
|||||||
@@ -202,4 +202,20 @@ impl super::WebUser for super::account_json::UserData {
|
|||||||
chats: chat_list,
|
chats: chat_list,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn fedi_info(&self) -> Result<super::FederationWebData, Box<dyn std::error::Error>> {
|
||||||
|
let servers = self.db.federations_all().await?;
|
||||||
|
Ok(super::FederationWebData {
|
||||||
|
federations: servers.into_iter()
|
||||||
|
.map(|server| super::FederationWebDetails {
|
||||||
|
id: server.id,
|
||||||
|
domain: server.domain,
|
||||||
|
auth: server.auth,
|
||||||
|
society: server.society,
|
||||||
|
last_used: server.last_used_time,
|
||||||
|
first_used: server.creation_time,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -943,6 +943,13 @@ impl Database {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn federations_all(&self) -> Result<Vec<crate::schema::federation::Model>, sea_orm::DbErr> {
|
||||||
|
crate::schema::federation::Entity::find()
|
||||||
|
.order_by_asc(crate::schema::federation::Column::Id)
|
||||||
|
.all(self.orm.as_ref())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn insert_federation(&self, entity: crate::schema::federation::ActiveModel) -> Result<crate::schema::federation::Model, sea_orm::DbErr> {
|
pub async fn insert_federation(&self, entity: crate::schema::federation::ActiveModel) -> Result<crate::schema::federation::Model, sea_orm::DbErr> {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
assert!(matches!(entity.id, sea_orm::ActiveValue::NotSet));
|
assert!(matches!(entity.id, sea_orm::ActiveValue::NotSet));
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.service(web::user_federation::post_add)
|
.service(web::user_federation::post_add)
|
||||||
.service(web::user_federation::post_off)
|
.service(web::user_federation::post_off)
|
||||||
.service(web::user_federation::post_on)
|
.service(web::user_federation::post_on)
|
||||||
|
.service(web::federation::get)
|
||||||
.service(api::config::get)
|
.service(api::config::get)
|
||||||
.service(api::urls::get)
|
.service(api::urls::get)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ pub async fn dashboard_impl(handlebars_ref: Data<handlebars::Handlebars<'_>>, au
|
|||||||
match super::try_auth_user(user_opt, auth.as_ref(), &req).await? {
|
match super::try_auth_user(user_opt, auth.as_ref(), &req).await? {
|
||||||
super::LoginReturn::AuthFail(resp) => Ok(resp),
|
super::LoginReturn::AuthFail(resp) => Ok(resp),
|
||||||
super::LoginReturn::Success(user) => {
|
super::LoginReturn::Success(user) => {
|
||||||
// TODO
|
|
||||||
log::debug!("Rendering user's dashboard");
|
log::debug!("Rendering user's dashboard");
|
||||||
let creation_time = user.creation();
|
let creation_time = user.creation();
|
||||||
let creation_time_chrono = chrono::DateTime::<chrono::Utc>::from_timestamp_secs(creation_time).unwrap_or_default();
|
let creation_time_chrono = chrono::DateTime::<chrono::Utc>::from_timestamp_secs(creation_time).unwrap_or_default();
|
||||||
|
|||||||
85
rc_society/src/web/federation.rs
Normal file
85
rc_society/src/web/federation.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use actix_web::{get, web::Data, Responder, HttpRequest};
|
||||||
|
use actix_identity::Identity;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use crate::web::{LoginReturn, try_auth_user, render_ok, render_err};
|
||||||
|
|
||||||
|
const FORM_NAME: &str = "federation";
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct RenderData {
|
||||||
|
display_name: String,
|
||||||
|
public_id: String,
|
||||||
|
instances: Vec<FederatedServer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct FederatedServer {
|
||||||
|
id: i32,
|
||||||
|
domain: String,
|
||||||
|
auth: String,
|
||||||
|
society: String,
|
||||||
|
last_used_unix: i64,
|
||||||
|
last_used_iso: String,
|
||||||
|
first_used_unix: i64,
|
||||||
|
first_used_iso: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/federation")]
|
||||||
|
pub async fn get(handlebars_ref: Data<handlebars::Handlebars<'_>>, auth: Data<Box<oj_rc_core::UserImpl>>, user_opt: Option<Identity>, req: HttpRequest) -> Result<impl Responder, actix_web::error::Error> {
|
||||||
|
match try_auth_user(user_opt, auth.as_ref(), &req).await? {
|
||||||
|
LoginReturn::AuthFail(resp) => Ok(resp),
|
||||||
|
LoginReturn::Success(user) => {
|
||||||
|
let fedi_info = match build_fedi_info(user.as_ref()).await {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
let html = render_err(
|
||||||
|
RenderData {
|
||||||
|
display_name: user.display_name().to_owned(),
|
||||||
|
public_id: user.public_id().to_owned(),
|
||||||
|
instances: Vec::default(),
|
||||||
|
},
|
||||||
|
e.to_string(),
|
||||||
|
handlebars_ref.as_ref(),
|
||||||
|
FORM_NAME,
|
||||||
|
);
|
||||||
|
return Ok(
|
||||||
|
html
|
||||||
|
.respond_to(&req)
|
||||||
|
.map_into_boxed_body()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let html = render_ok(
|
||||||
|
RenderData {
|
||||||
|
display_name: user.display_name().to_owned(),
|
||||||
|
public_id: user.public_id().to_owned(),
|
||||||
|
instances: fedi_info,
|
||||||
|
},
|
||||||
|
handlebars_ref.as_ref(),
|
||||||
|
FORM_NAME,
|
||||||
|
);
|
||||||
|
Ok(
|
||||||
|
html
|
||||||
|
.respond_to(&req)
|
||||||
|
.map_into_boxed_body()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn build_fedi_info(user: &dyn oj_rc_core::persist::user::WebUser) -> Result<Vec<FederatedServer>, Box<dyn std::error::Error>> {
|
||||||
|
let info = user.fedi_info().await?;
|
||||||
|
Ok(info.federations.into_iter()
|
||||||
|
.map(|instance| FederatedServer {
|
||||||
|
id: instance.id,
|
||||||
|
domain: instance.domain,
|
||||||
|
auth: instance.auth,
|
||||||
|
society: instance.society,
|
||||||
|
last_used_unix: instance.last_used,
|
||||||
|
last_used_iso: chrono::DateTime::<chrono::Utc>::from_timestamp_secs(instance.last_used).unwrap_or_default().to_rfc3339(),
|
||||||
|
first_used_unix: instance.first_used,
|
||||||
|
first_used_iso: chrono::DateTime::<chrono::Utc>::from_timestamp_secs(instance.first_used).unwrap_or_default().to_rfc3339(),
|
||||||
|
}).collect()
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ pub mod index;
|
|||||||
pub mod garage;
|
pub mod garage;
|
||||||
pub mod user_federation;
|
pub mod user_federation;
|
||||||
pub mod logout;
|
pub mod logout;
|
||||||
|
pub mod federation;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use actix_web::{web::{Html, Redirect}, Responder};
|
use actix_web::{web::{Html, Redirect}, Responder};
|
||||||
|
|||||||
Reference in New Issue
Block a user