Enhance README with CLI room checking instructions and implement room name resolution in command handling. Updated handle_init_command and handle_join_command to use resolved room names, improving room access validation. Added CLI command handling in main.rs to list available rooms from the database or fallback configuration.

This commit is contained in:
Torsten Schulz (local)
2026-03-04 17:55:53 +01:00
parent aca290f1d0
commit 9478e6a91a
4 changed files with 105 additions and 23 deletions

View File

@@ -148,7 +148,7 @@ async fn handle_init_command(
let room_name = normalize_room_name(command.room.as_deref().unwrap_or("lobby"));
let room_password = command.password.clone().unwrap_or_default();
let (token, user_name) = {
let (token, user_name, actual_room_name) = {
let mut guard = state.write().await;
if guard.logged_in_names.contains(&requested_user_name)
&& guard
@@ -160,8 +160,13 @@ async fn handle_init_command(
send_error(client_id, Arc::clone(&state), "loggedin").await;
return;
}
let Some(resolved_room_name) = resolve_room_name(&guard, &room_name) else {
drop(guard);
send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await;
return;
};
if !room_access_allowed(
guard.room_meta.get(&room_name),
guard.room_meta.get(&resolved_room_name),
&profile.display_name,
profile.falukant_user_id,
profile.age,
@@ -187,7 +192,7 @@ async fn handle_init_command(
client.age = profile.age;
client.rights = profile.rights.clone();
client.logged_in = true;
client.room = room_name.clone();
client.room = resolved_room_name.clone();
let mut new_token = None;
if client.token.is_none() {
@@ -218,8 +223,12 @@ async fn handle_init_command(
members.remove(&client_id);
}
}
guard.rooms.entry(room_name.clone()).or_default().insert(client_id);
(token, user_name)
guard
.rooms
.entry(resolved_room_name.clone())
.or_default()
.insert(client_id);
(token, user_name, resolved_room_name)
};
state::send_to_client(
@@ -232,13 +241,13 @@ async fn handle_init_command(
state::send_to_client(
client_id,
Arc::clone(&state),
json!({"type":5, "message":"room_entered", "to": room_name}),
json!({"type":5, "message":"room_entered", "to": actual_room_name}),
)
.await;
state::broadcast_room(
&room_name,
&actual_room_name,
Arc::clone(&state),
json!({"type":"system","message": format!("{user_name} joined {room_name}")}),
json!({"type":"system","message": format!("{user_name} joined {actual_room_name}")}),
Some(client_id),
)
.await;
@@ -255,8 +264,13 @@ async fn handle_join_command(
}
let room = normalize_room_name(command.room.as_deref().or(command.name.as_deref()).unwrap_or("lobby"));
let password = command.password.clone().unwrap_or_default();
let (from_room, user_name) = {
let (from_room, user_name, actual_room_name) = {
let mut guard = state.write().await;
let Some(resolved_room) = resolve_room_name(&guard, &room) else {
drop(guard);
send_error(client_id, Arc::clone(&state), "room_not_found_or_join_failed").await;
return;
};
let (name_for_check, falukant_user_id, age) = {
let Some(client) = guard.clients.get(&client_id) else {
return;
@@ -264,7 +278,7 @@ async fn handle_join_command(
(client.user_name.clone(), client.falukant_user_id, client.age)
};
if !room_access_allowed(
guard.room_meta.get(&room),
guard.room_meta.get(&resolved_room),
&name_for_check,
falukant_user_id,
age,
@@ -279,14 +293,14 @@ async fn handle_join_command(
};
let from_room = client.room.clone();
let user_name = client.user_name.clone();
client.room = room.clone();
client.room = resolved_room.clone();
if !from_room.is_empty() {
if let Some(members) = guard.rooms.get_mut(&from_room) {
members.remove(&client_id);
}
}
guard.rooms.entry(room.clone()).or_default().insert(client_id);
(from_room, user_name)
guard.rooms.entry(resolved_room.clone()).or_default().insert(client_id);
(from_room, user_name, resolved_room)
};
if !from_room.is_empty() && from_room != room {
@@ -301,7 +315,7 @@ async fn handle_join_command(
state::send_to_client(
client_id,
Arc::clone(&state),
json!({"type":5, "message":"room_entered", "to": room}),
json!({"type":5, "message":"room_entered", "to": actual_room_name}),
)
.await;
state::send_room_list(client_id, Arc::clone(&state)).await;
@@ -754,7 +768,7 @@ async fn handle_unknown_command(client_id: ClientId, command: &Command, state: A
fn room_access_allowed(
room_meta: Option<&RoomMeta>,
user_name: &str,
_user_name: &str,
falukant_user_id: Option<i32>,
age: Option<i32>,
provided_password: &str,
@@ -792,17 +806,21 @@ fn room_access_allowed(
}
}
if !room.is_public {
if room.owner_id.is_none() {
return false;
}
if user_name.trim().is_empty() {
return false;
}
}
true
}
fn resolve_room_name(state: &ChatState, requested: &str) -> Option<String> {
if state.room_meta.contains_key(requested) {
return Some(requested.to_string());
}
let requested_lower = requested.to_lowercase();
state
.room_meta
.keys()
.find(|name| name.to_lowercase() == requested_lower)
.cloned()
}
fn normalize_room_name(input: &str) -> String {
let trimmed = input.trim();
if trimmed.is_empty() {

View File

@@ -38,6 +38,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
allowed_users: db::parse_allowed_users(),
db_client,
});
if handle_cli_commands().as_deref() == Some("--list-rooms") {
print_rooms_for_cli(&config).await?;
return Ok(());
}
let rooms = db::load_room_configs(&config).await.unwrap_or_else(|_| {
vec![types::RoomMeta {
name: "lobby".to_string(),
@@ -212,6 +216,47 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(())
}
fn handle_cli_commands() -> Option<String> {
env::args().nth(1)
}
async fn print_rooms_for_cli(
config: &ServerConfig,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let rooms = db::load_room_configs(config)
.await
.map_err(std::io::Error::other)?;
println!(
"yourchat2 rooms source: {}",
if config.db_client.is_some() { "database" } else { "fallback" }
);
println!(
"{:<30} {:<8} {:<8} {:<8} {:<10}",
"name", "public", "min_age", "max_age", "password"
);
println!("{}", "-".repeat(72));
for room in rooms {
println!(
"{:<30} {:<8} {:<8} {:<8} {:<10}",
room.name,
if room.is_public { "yes" } else { "no" },
room.min_age
.map(|v| v.to_string())
.unwrap_or_else(|| "-".to_string()),
room.max_age
.map(|v| v.to_string())
.unwrap_or_else(|| "-".to_string()),
if room.password.as_deref().unwrap_or("").is_empty() {
"none"
} else {
"set"
},
);
}
Ok(())
}
async fn handle_client<S>(
stream: S,
state: Arc<RwLock<ChatState>>,

View File

@@ -78,6 +78,7 @@ pub(crate) struct DiceGame {
pub(crate) total_scores: HashMap<ClientId, i32>,
}
#[allow(dead_code)]
#[derive(Clone, Debug, Default)]
pub(crate) struct RoomMeta {
pub(crate) name: String,