add: patch method on stream key

This commit is contained in:
2026-07-11 14:35:35 +01:00
parent de622816cc
commit aeaed9f670
4 changed files with 72 additions and 3 deletions
Generated
+1
View File
@@ -2879,6 +2879,7 @@ dependencies = [
"migration",
"opus",
"rand 0.10.1",
"regex",
"rml_rtmp",
"rubato",
"sea-orm",
+11
View File
@@ -79,3 +79,14 @@ impl Entity {
.await
}
}
impl ActiveModel {
pub async fn change_label_value(
mut self,
db: &DatabaseConnection,
value: String,
) -> Result<Model, DbErr> {
self.label = Set(value);
self.update(db).await
}
}
+1
View File
@@ -35,3 +35,4 @@ symphonia = { version = "0.5", features = ["aac"] }
opus = "0.3.1"
rubato = "3.0.0"
chrono = "0.4.45"
regex = "1"
+59 -3
View File
@@ -1,11 +1,21 @@
use std::{
net::SocketAddr,
sync::{
Arc,
Arc, LazyLock,
atomic::{AtomicI32, Ordering},
},
};
use regex::Regex;
// The Rust `regex` crate is guaranteed linear-time and therefore does NOT
// support lookaround, so the JS source pattern
// /^(?=.*[A-Za-z])[A-Za-z0-9_-]{1,67}$/
// cannot be ported verbatim. The `(?=.*[A-Za-z])` lookahead only means
// "must contain at least one letter" — we drop it from the pattern and
// enforce that condition with a separate `.chars().any(..)` check below.
static KEY_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^[A-Za-z0-9 _-]{1,67}$").unwrap());
use axum::{
Json, Router,
body::{Body, Bytes},
@@ -20,7 +30,9 @@ use axum::{
};
use chrono::{DateTime, Utc};
use entity::{auth_session, stream_key, stream_session, users};
use sea_orm::{DatabaseConnection, EntityTrait, QueryFilter, prelude::DateTimeUtc};
use sea_orm::{
DatabaseConnection, EntityTrait, IntoActiveModel, QueryFilter, prelude::DateTimeUtc,
};
use serde::{Deserialize, Serialize};
use tokio::{
net::TcpListener,
@@ -83,7 +95,9 @@ impl HttpServer {
.route("/api/user", post(create_user_handler))
.route(
"/api/stream-key",
post(create_stream_key_handler).get(get_all_stream_keys),
post(create_stream_key_handler)
.get(get_all_stream_keys)
.patch(edit_stream_key),
)
// .route("/api/admin/server_stats", get(todo!()))
.route("/api/whip", post(handle_whip_injest))
@@ -112,6 +126,48 @@ struct StreamListing {
started_at: DateTime<Utc>, //UNIX TIMESTAMP
}
#[derive(Deserialize)]
struct EditStreamKeyRequest {
id: i32,
new: String,
}
async fn edit_stream_key(
State(state): State<Arc<HttpServer>>,
auth: AuthUser,
Json(payload): Json<EditStreamKeyRequest>,
) -> impl IntoResponse {
// Trim surrounding whitespace so labels aren't stored with leading/trailing spaces.
let new_label = payload.new.trim();
// Length (1..=67) and allowed charset ([A-Za-z0-9 _-], space included).
if !KEY_RE.is_match(new_label) {
return (StatusCode::BAD_REQUEST, "invalid label").into_response();
}
// Replaces the JS lookahead: label must contain at least one letter.
if !new_label.chars().any(|c| c.is_ascii_alphabetic()) {
return (StatusCode::BAD_REQUEST, "label must contain a letter").into_response();
}
let stream_key_id_result = stream_key::Entity::find_by_id(payload.id)
.one(&state.db)
.await;
if stream_key_id_result.is_err() {
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
let stream_key = stream_key_id_result.unwrap().unwrap();
// Do they own the stream key
if stream_key.user_id != auth.0.id {
return StatusCode::FORBIDDEN.into_response();
}
stream_key
.into_active_model()
.change_label_value(&state.db, new_label.to_string())
.await
.unwrap();
StatusCode::OK.into_response()
}
async fn catalog_handler(State(state): State<Arc<HttpServer>>) -> impl IntoResponse {
let streams = stream_session::Model::get_all_active_sessions(&state.db)
.await