197 lines
6.4 KiB
Rust
197 lines
6.4 KiB
Rust
use spacetimedb::{Identity, ReducerContext, Table};
|
|
|
|
mod reducers;
|
|
mod tables;
|
|
mod utils;
|
|
mod views;
|
|
|
|
pub use reducers::*;
|
|
pub use tables::*;
|
|
pub use utils::*;
|
|
pub use views::*;
|
|
|
|
pub const SYSTEM_IDENTITY: &str = "0000000000000000000000000000000000000000000000000000000000000000";
|
|
|
|
#[spacetimedb::reducer(init)]
|
|
pub fn init(ctx: &ReducerContext) {
|
|
let system_identity = Identity::from_hex(SYSTEM_IDENTITY).unwrap();
|
|
|
|
// Create system user if not exists
|
|
if ctx.db.user().identity().find(system_identity).is_none() {
|
|
ctx.db.user().insert(User {
|
|
identity: system_identity,
|
|
name: Some("Zep".to_string()),
|
|
online: true,
|
|
issuer: None,
|
|
subject: None,
|
|
anonymous: false,
|
|
avatar_id: None,
|
|
banner_id: None,
|
|
biography: Some("I am the Zep system assistant.".to_string()),
|
|
status: Some("Online".to_string()),
|
|
public_key: None,
|
|
});
|
|
}
|
|
|
|
if ctx
|
|
.db
|
|
.system_configuration()
|
|
.key()
|
|
.find("max_message_length".to_string())
|
|
.is_none()
|
|
{
|
|
ctx.db.system_configuration().insert(SystemConfiguration {
|
|
key: "max_message_length".to_string(),
|
|
value: "262144".to_string(),
|
|
});
|
|
}
|
|
|
|
if Table::iter(ctx.db.server()).next().is_none() {
|
|
let s = ctx.db.server().insert(Server {
|
|
id: 0,
|
|
name: "Zep".to_string(),
|
|
owner: None,
|
|
avatar_id: None,
|
|
channels: Vec::new(),
|
|
public: true,
|
|
});
|
|
let c1 = ctx.db.channel().insert(Channel {
|
|
id: 0,
|
|
server_id: s.id,
|
|
name: "general".to_string(),
|
|
kind: ChannelKind::Text,
|
|
});
|
|
let c2 = ctx.db.channel().insert(Channel {
|
|
id: 0,
|
|
server_id: s.id,
|
|
name: "Voice General".to_string(),
|
|
kind: ChannelKind::Voice,
|
|
});
|
|
|
|
let mut s = ctx.db.server().id().find(s.id).unwrap();
|
|
s.channels.push(ChannelMetadata {
|
|
id: c1.id,
|
|
name: c1.name,
|
|
kind: c1.kind,
|
|
});
|
|
s.channels.push(ChannelMetadata {
|
|
id: c2.id,
|
|
name: c2.name,
|
|
kind: c2.kind,
|
|
});
|
|
ctx.db.server().id().update(s.clone());
|
|
|
|
// Grant access to system user
|
|
sync_server_access(&ctx.db, system_identity, s.id);
|
|
}
|
|
}
|
|
|
|
#[spacetimedb::reducer(client_connected)]
|
|
pub fn on_connect(ctx: &ReducerContext) {
|
|
log::info!("on_connect START: identity={}", ctx.sender().to_hex());
|
|
|
|
// Extract potential name from OIDC if available
|
|
let mut initial_name = None;
|
|
let mut is_anon = true;
|
|
if let Some(jwt) = ctx.sender_auth().jwt() {
|
|
let sub = jwt.subject();
|
|
let issuer = jwt.issuer();
|
|
// Use first 8 chars of sub if it's a long string/UUID
|
|
initial_name = Some(if sub.len() > 12 { sub[..8].to_string() } else { sub.to_string() });
|
|
is_anon = issuer.contains("localhost");
|
|
}
|
|
|
|
if let Some(mut user) = ctx.db.user().identity().find(ctx.sender()) {
|
|
user.online = true;
|
|
// Update name from OIDC if current user has no name
|
|
if user.name.is_none() && initial_name.is_some() {
|
|
user.name = initial_name;
|
|
}
|
|
user.anonymous = is_anon;
|
|
ctx.db.user().identity().update(user);
|
|
} else {
|
|
ctx.db.user().insert(User {
|
|
identity: ctx.sender(),
|
|
name: initial_name,
|
|
online: true,
|
|
issuer: None,
|
|
subject: None,
|
|
anonymous: is_anon,
|
|
avatar_id: None,
|
|
banner_id: None,
|
|
biography: None,
|
|
status: None,
|
|
public_key: None,
|
|
});
|
|
|
|
// Minimal auto-join
|
|
join_server(ctx, 1);
|
|
|
|
// System Welcome DM
|
|
let system_identity = Identity::from_hex(SYSTEM_IDENTITY).unwrap();
|
|
let channel_id = internal_open_direct_message(&ctx.db, system_identity, ctx.sender());
|
|
let welcome_text = "Welcome to Zep! We're glad to have you here.\n\nZep is a decentralized, private, and fast chat service built on SpacetimeDB. You can join servers, create channels, and message friends directly—all with the security and performance of a modern relational backend.";
|
|
internal_send_message(
|
|
&ctx.db,
|
|
system_identity,
|
|
channel_id,
|
|
welcome_text.to_string(),
|
|
ctx.timestamp,
|
|
None,
|
|
vec![],
|
|
false,
|
|
);
|
|
}
|
|
|
|
// High Performance: Sync all channel access for this user
|
|
for member in ctx.db.server_member().identity().filter(ctx.sender()) {
|
|
sync_server_access(&ctx.db, ctx.sender(), member.server_id);
|
|
}
|
|
|
|
sync_server_member_info(&ctx.db, ctx.sender());
|
|
|
|
log::info!("on_connect END: identity={}", ctx.sender().to_hex());
|
|
}
|
|
|
|
#[spacetimedb::reducer(client_disconnected)]
|
|
pub fn on_disconnect(ctx: &ReducerContext) {
|
|
log::info!("on_disconnect: identity={}", ctx.sender().to_hex());
|
|
if let Some(mut user) = ctx.db.user().identity().find(ctx.sender()) {
|
|
user.online = false;
|
|
ctx.db.user().identity().update(user);
|
|
}
|
|
|
|
sync_server_member_info(&ctx.db, ctx.sender());
|
|
|
|
if let Some(ta) = ctx.db.typing_activity().identity().find(ctx.sender()) {
|
|
ctx.db.typing_activity().delete(ta);
|
|
}
|
|
|
|
clear_user_presence(&ctx.db, ctx.sender());
|
|
}
|
|
|
|
#[spacetimedb::reducer]
|
|
pub fn update_auth_info(ctx: &ReducerContext) {
|
|
log::info!("update_auth_info: identity={}", ctx.sender().to_hex());
|
|
if let Some(mut user) = ctx.db.user().identity().find(ctx.sender()) {
|
|
if let Some(jwt) = ctx.sender_auth().jwt() {
|
|
let sub = jwt.subject();
|
|
let issuer = jwt.issuer();
|
|
user.issuer = Some(issuer.to_string());
|
|
user.subject = Some(sub.to_string());
|
|
|
|
// Flag as anonymous if issuer is localhost
|
|
user.anonymous = issuer.contains("localhost");
|
|
|
|
// Also update name if they don't have a custom one yet
|
|
if user.name.is_none() {
|
|
user.name = Some(if sub.len() > 12 { sub[..8].to_string() } else { sub.to_string() });
|
|
}
|
|
|
|
log::info!("update_auth_info: updated user with OIDC info (anon={})", user.anonymous);
|
|
ctx.db.user().identity().update(user);
|
|
sync_server_member_info(&ctx.db, ctx.sender());
|
|
}
|
|
}
|
|
}
|