mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-03 00:47:53 +00:00
debug logs: new field, new queries, new APIs
This commit is contained in:
1 parent
cbd3710f4f
commit
197ec95012
32 files changed
+848
-56
No files matched your search
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE debug_logs
|
||||
DROP COLUMN maybe_log_level;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE debug_logs
|
||||
ADD COLUMN maybe_log_level VARCHAR(16) DEFAULT NULL AFTER debug_log_type;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE debug_logs
|
||||
DROP INDEX index_maybe_log_level;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE debug_logs
|
||||
ADD INDEX index_maybe_log_level (maybe_log_level);
|
||||
@@ -13,6 +13,9 @@ CREATE TABLE debug_logs (
|
||||
-- The type of debug log entry.
|
||||
debug_log_type VARCHAR(24) NOT NULL,
|
||||
|
||||
-- The severity level of the log entry (info, warn, error, debug, trace).
|
||||
maybe_log_level VARCHAR(16) DEFAULT NULL,
|
||||
|
||||
-- The user who created the log entry (if any).
|
||||
maybe_creator_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
@@ -25,6 +28,7 @@ CREATE TABLE debug_logs (
|
||||
PRIMARY KEY (id),
|
||||
KEY index_event_token (event_token),
|
||||
KEY index_debug_log_type (debug_log_type),
|
||||
KEY index_maybe_log_level (maybe_log_level),
|
||||
KEY index_maybe_creator_user_token (maybe_creator_user_token),
|
||||
KEY index_created_at (created_at)
|
||||
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_derive::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use enums::by_table::debug_logs::debug_log_level::DebugLogLevel;
|
||||
use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
/// A single debug log row.
|
||||
/// Shared by the token-scoped and user-scoped moderation list endpoints.
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModerationDebugLogEntry {
|
||||
pub id: u64,
|
||||
pub event_token: DebugLogEventToken,
|
||||
pub debug_log_type: DebugLogType,
|
||||
pub maybe_log_level: Option<DebugLogLevel>,
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub message: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod debug_log_entry;
|
||||
pub mod moderation_list_all_debug_logs;
|
||||
pub mod moderation_list_debug_logs_for_token;
|
||||
pub mod moderation_list_debug_logs_for_user;
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use enums::by_table::debug_logs::debug_log_level::DebugLogLevel;
|
||||
use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
use enums::error::enum_error::EnumError;
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
pub const MODERATION_LIST_ALL_DEBUG_LOGS_PATH: &str = "/v1/moderation/debug_logs/list_all";
|
||||
|
||||
// ── Query params ──
|
||||
|
||||
#[derive(Deserialize, IntoParams, ToSchema)]
|
||||
pub struct ModerationListAllDebugLogsQueryParams {
|
||||
/// Optional comma-separated list of severity levels to include,
|
||||
/// eg. `severities=error,warn`. Omit to include all rows.
|
||||
pub severities: Option<String>,
|
||||
|
||||
/// Cursor for pagination. Pass the `next_cursor` from a previous response.
|
||||
pub cursor: Option<u64>,
|
||||
|
||||
/// Max rows per page (default 50, max 200).
|
||||
pub limit: Option<u32>,
|
||||
}
|
||||
|
||||
impl ModerationListAllDebugLogsQueryParams {
|
||||
/// Parse the comma-separated `severities` parameter.
|
||||
/// Returns `None` when the parameter is absent or empty.
|
||||
pub fn parsed_severities(&self) -> Result<Option<Vec<DebugLogLevel>>, EnumError> {
|
||||
let raw = match self.severities.as_deref() {
|
||||
Some(value) if !value.trim().is_empty() => value,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
let levels = raw
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.filter(|part| !part.is_empty())
|
||||
.map(DebugLogLevel::from_str)
|
||||
.collect::<Result<Vec<DebugLogLevel>, EnumError>>()?;
|
||||
|
||||
Ok(if levels.is_empty() { None } else { Some(levels) })
|
||||
}
|
||||
}
|
||||
|
||||
// ── Response ──
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModerationListAllDebugLogsSuccessResponse {
|
||||
pub success: bool,
|
||||
/// Most recent first.
|
||||
pub debug_logs: Vec<ModerationListAllDebugLogsEntry>,
|
||||
/// Cursor for the next page. `None` if there are no more results.
|
||||
pub next_cursor: Option<u64>,
|
||||
}
|
||||
|
||||
/// A debug log row with the creator user joined in (when one exists).
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModerationListAllDebugLogsEntry {
|
||||
pub id: u64,
|
||||
pub event_token: DebugLogEventToken,
|
||||
pub debug_log_type: DebugLogType,
|
||||
pub maybe_log_level: Option<DebugLogLevel>,
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub message: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub maybe_user: Option<ModerationListAllDebugLogsUser>,
|
||||
}
|
||||
|
||||
/// The creator user of a debug log row.
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModerationListAllDebugLogsUser {
|
||||
pub user_token: UserToken,
|
||||
pub display_name: String,
|
||||
pub username: String,
|
||||
pub gravatar_hash: String,
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
|
||||
use crate::moderation::debug_logs::debug_log_entry::ModerationDebugLogEntry;
|
||||
|
||||
pub const MODERATION_LIST_DEBUG_LOGS_FOR_TOKEN_PATH: &str = "/v1/moderation/debug_logs/list/{token}";
|
||||
|
||||
// ── Path params ──
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ModerationListDebugLogsForTokenPathInfo {
|
||||
pub token: DebugLogEventToken,
|
||||
}
|
||||
|
||||
// ── Query params ──
|
||||
|
||||
#[derive(Deserialize, IntoParams, ToSchema)]
|
||||
pub struct ModerationListDebugLogsForTokenQueryParams {
|
||||
pub limit: Option<u32>,
|
||||
}
|
||||
|
||||
// ── Response ──
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModerationListDebugLogsForTokenSuccessResponse {
|
||||
pub success: bool,
|
||||
pub debug_logs: Vec<ModerationDebugLogEntry>,
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::moderation::debug_logs::debug_log_entry::ModerationDebugLogEntry;
|
||||
|
||||
pub const MODERATION_LIST_DEBUG_LOGS_FOR_USER_PATH: &str = "/v1/moderation/debug_logs/user_list/{user_token}";
|
||||
|
||||
// ── Path params ──
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ModerationListDebugLogsForUserPathInfo {
|
||||
pub user_token: UserToken,
|
||||
}
|
||||
|
||||
// ── Query params ──
|
||||
|
||||
#[derive(Deserialize, IntoParams, ToSchema)]
|
||||
pub struct ModerationListDebugLogsForUserQueryParams {
|
||||
/// Cursor for pagination. Pass the `next_cursor` from a previous response.
|
||||
pub cursor: Option<u64>,
|
||||
|
||||
/// Max rows per page (default 50, max 200).
|
||||
pub limit: Option<u32>,
|
||||
}
|
||||
|
||||
// ── Response ──
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModerationListDebugLogsForUserSuccessResponse {
|
||||
pub success: bool,
|
||||
/// Most recent first.
|
||||
pub debug_logs: Vec<ModerationDebugLogEntry>,
|
||||
/// Cursor for the next page. `None` if there are no more results.
|
||||
pub next_cursor: Option<u64>,
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod alerts;
|
||||
pub mod debug_logs;
|
||||
pub mod jobs;
|
||||
pub mod user;
|
||||
pub mod user_referrals;
|
||||
|
||||
+5
-1
@@ -3,6 +3,7 @@ use std::marker::PhantomData;
|
||||
use log::warn;
|
||||
use sqlx::{Executor, MySql, QueryBuilder};
|
||||
|
||||
use enums::by_table::debug_logs::debug_log_level::DebugLogLevel;
|
||||
use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
@@ -31,6 +32,7 @@ where
|
||||
/// Callers should use `serde_json::to_string(...)` before constructing this.
|
||||
pub struct DebugLogEvent {
|
||||
pub debug_log_type: DebugLogType,
|
||||
pub maybe_log_level: Option<DebugLogLevel>,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ where
|
||||
let maybe_user_token_str = args.maybe_creator_user_token.map(|t| t.as_str().to_string());
|
||||
|
||||
let mut query_builder = QueryBuilder::new(
|
||||
"INSERT INTO debug_logs (event_token, debug_log_type, maybe_creator_user_token, message) VALUES "
|
||||
"INSERT INTO debug_logs (event_token, debug_log_type, maybe_log_level, maybe_creator_user_token, message) VALUES "
|
||||
);
|
||||
|
||||
for (i, event) in args.events.iter().enumerate() {
|
||||
@@ -65,6 +67,8 @@ where
|
||||
query_builder.push(", ");
|
||||
query_builder.push_bind(event.debug_log_type.to_str());
|
||||
query_builder.push(", ");
|
||||
query_builder.push_bind(event.maybe_log_level.map(|l| l.to_str()));
|
||||
query_builder.push(", ");
|
||||
query_builder.push_bind(&maybe_user_token_str);
|
||||
query_builder.push(", ");
|
||||
query_builder.push_bind(&event.message);
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::marker::PhantomData;
|
||||
|
||||
use sqlx::{Executor, MySql};
|
||||
|
||||
use enums::by_table::debug_logs::debug_log_level::DebugLogLevel;
|
||||
use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
@@ -20,6 +21,9 @@ where
|
||||
/// The type of debug log event.
|
||||
pub debug_log_type: DebugLogType,
|
||||
|
||||
/// The severity level of the log event (if any).
|
||||
pub maybe_log_level: Option<DebugLogLevel>,
|
||||
|
||||
/// The log message body (pre-serialized JSON or any text).
|
||||
pub message: &'e str,
|
||||
|
||||
@@ -47,11 +51,13 @@ INSERT INTO debug_logs
|
||||
SET
|
||||
event_token = ?,
|
||||
debug_log_type = ?,
|
||||
maybe_log_level = ?,
|
||||
maybe_creator_user_token = ?,
|
||||
message = ?
|
||||
"#,
|
||||
event_token.as_str(),
|
||||
args.debug_log_type.to_str(),
|
||||
args.maybe_log_level.map(|l| l.to_str()),
|
||||
args.maybe_creator_user_token.map(|t| t.as_str()),
|
||||
args.message,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use sqlx::mysql::MySqlRow;
|
||||
use sqlx::{Executor, MySql, QueryBuilder, Row};
|
||||
|
||||
use enums::by_table::debug_logs::debug_log_level::DebugLogLevel;
|
||||
use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
const DEFAULT_LIMIT: u32 = 50;
|
||||
const MAX_LIMIT: u32 = 200;
|
||||
|
||||
pub struct ListAllDebugLogsArgs<'c, E>
|
||||
where
|
||||
E: Executor<'c, Database = MySql>,
|
||||
{
|
||||
/// If provided, only rows with one of these log levels are returned.
|
||||
/// (Rows with a NULL log level are excluded when filtering.)
|
||||
pub maybe_log_levels: Option<Vec<DebugLogLevel>>,
|
||||
|
||||
/// Cursor for pagination: only rows with `id` strictly below this are
|
||||
/// returned. Pass the `next_cursor` from a previous page.
|
||||
pub maybe_id_cursor: Option<u64>,
|
||||
|
||||
pub limit: Option<u32>,
|
||||
pub mysql_executor: E,
|
||||
pub phantom: PhantomData<&'c E>,
|
||||
}
|
||||
|
||||
pub struct ListAllDebugLogsResult {
|
||||
pub debug_logs: Vec<AllDebugLogRow>,
|
||||
/// Cursor for the next page. `None` if there are no more results.
|
||||
pub next_cursor: Option<u64>,
|
||||
}
|
||||
|
||||
/// A debug log row left-outer-joined against the creator user (if any).
|
||||
#[derive(Debug)]
|
||||
pub struct AllDebugLogRow {
|
||||
pub id: u64,
|
||||
pub event_token: DebugLogEventToken,
|
||||
pub debug_log_type: DebugLogType,
|
||||
pub maybe_log_level: Option<DebugLogLevel>,
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub message: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
|
||||
// Joined user fields (present when the creator user exists).
|
||||
pub maybe_user_display_name: Option<String>,
|
||||
pub maybe_user_username: Option<String>,
|
||||
pub maybe_user_gravatar_hash: Option<String>,
|
||||
}
|
||||
|
||||
/// List all debug logs, most recent first, cursor-paginated by `id`,
|
||||
/// optionally scoped to a set of log levels.
|
||||
///
|
||||
/// NB: Uses `QueryBuilder` (not the compile-time macros) because of the
|
||||
/// dynamic `WHERE maybe_log_level IN (...)` clause.
|
||||
pub async fn list_all_debug_logs<'c, E>(
|
||||
args: ListAllDebugLogsArgs<'c, E>,
|
||||
) -> Result<ListAllDebugLogsResult, sqlx::Error>
|
||||
where
|
||||
E: Executor<'c, Database = MySql>,
|
||||
{
|
||||
let limit = args.limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT);
|
||||
// Fetch limit + 1 so we can detect whether there's a next page.
|
||||
let fetch_limit = (limit + 1) as i64;
|
||||
|
||||
let id_cursor = args.maybe_id_cursor.unwrap_or(u64::MAX);
|
||||
|
||||
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new(
|
||||
r#"
|
||||
SELECT
|
||||
d.id,
|
||||
d.event_token,
|
||||
d.debug_log_type,
|
||||
d.maybe_log_level,
|
||||
d.maybe_creator_user_token,
|
||||
d.message,
|
||||
d.created_at,
|
||||
u.display_name as maybe_user_display_name,
|
||||
u.username as maybe_user_username,
|
||||
u.email_gravatar_hash as maybe_user_gravatar_hash
|
||||
FROM debug_logs d
|
||||
LEFT OUTER JOIN users u
|
||||
ON u.token = d.maybe_creator_user_token
|
||||
WHERE d.id < "#,
|
||||
);
|
||||
query_builder.push_bind(id_cursor);
|
||||
|
||||
if let Some(levels) = args.maybe_log_levels.as_ref().filter(|l| !l.is_empty()) {
|
||||
query_builder.push(" AND d.maybe_log_level IN (");
|
||||
let mut separated = query_builder.separated(", ");
|
||||
for level in levels {
|
||||
separated.push_bind(level.to_str());
|
||||
}
|
||||
separated.push_unseparated(")");
|
||||
}
|
||||
|
||||
query_builder.push(" ORDER BY d.id DESC LIMIT ");
|
||||
query_builder.push_bind(fetch_limit);
|
||||
|
||||
let raw_rows: Vec<MySqlRow> = query_builder
|
||||
.build()
|
||||
.fetch_all(args.mysql_executor)
|
||||
.await?;
|
||||
|
||||
let mut debug_logs = raw_rows
|
||||
.into_iter()
|
||||
.map(row_from_mysql_row)
|
||||
.collect::<Result<Vec<AllDebugLogRow>, sqlx::Error>>()?;
|
||||
|
||||
let next_cursor = if debug_logs.len() > limit as usize {
|
||||
debug_logs.truncate(limit as usize);
|
||||
debug_logs.last().map(|row| row.id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(ListAllDebugLogsResult {
|
||||
debug_logs,
|
||||
next_cursor,
|
||||
})
|
||||
}
|
||||
|
||||
fn row_from_mysql_row(row: MySqlRow) -> Result<AllDebugLogRow, sqlx::Error> {
|
||||
let created_at: DateTime<Utc> = row.try_get("created_at")?;
|
||||
|
||||
Ok(AllDebugLogRow {
|
||||
id: row.try_get("id")?,
|
||||
event_token: row.try_get("event_token")?,
|
||||
debug_log_type: row.try_get("debug_log_type")?,
|
||||
maybe_log_level: row.try_get("maybe_log_level")?,
|
||||
maybe_creator_user_token: row.try_get("maybe_creator_user_token")?,
|
||||
message: row.try_get("message")?,
|
||||
created_at,
|
||||
maybe_user_display_name: row.try_get("maybe_user_display_name")?,
|
||||
maybe_user_username: row.try_get("maybe_user_username")?,
|
||||
maybe_user_gravatar_hash: row.try_get("maybe_user_gravatar_hash")?,
|
||||
})
|
||||
}
|
||||
+5
@@ -3,6 +3,7 @@ use std::marker::PhantomData;
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use sqlx::{Executor, MySql};
|
||||
|
||||
use enums::by_table::debug_logs::debug_log_level::DebugLogLevel;
|
||||
use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
@@ -24,6 +25,7 @@ pub struct DebugLogRow {
|
||||
pub id: u64,
|
||||
pub event_token: DebugLogEventToken,
|
||||
pub debug_log_type: DebugLogType,
|
||||
pub maybe_log_level: Option<DebugLogLevel>,
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub message: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
@@ -34,6 +36,7 @@ struct RawDebugLogRow {
|
||||
id: u64,
|
||||
event_token: DebugLogEventToken,
|
||||
debug_log_type: DebugLogType,
|
||||
maybe_log_level: Option<DebugLogLevel>,
|
||||
maybe_creator_user_token: Option<UserToken>,
|
||||
message: String,
|
||||
created_at: NaiveDateTime,
|
||||
@@ -54,6 +57,7 @@ SELECT
|
||||
id as `id: u64`,
|
||||
event_token as `event_token: tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken`,
|
||||
debug_log_type as `debug_log_type: enums::by_table::debug_logs::debug_log_type::DebugLogType`,
|
||||
maybe_log_level as `maybe_log_level: enums::by_table::debug_logs::debug_log_level::DebugLogLevel`,
|
||||
maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
|
||||
message,
|
||||
created_at
|
||||
@@ -73,6 +77,7 @@ LIMIT ?
|
||||
id: row.id,
|
||||
event_token: row.event_token,
|
||||
debug_log_type: row.debug_log_type,
|
||||
maybe_log_level: row.maybe_log_level,
|
||||
maybe_creator_user_token: row.maybe_creator_user_token,
|
||||
message: row.message,
|
||||
created_at: row.created_at.and_utc(),
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use sqlx::{Executor, MySql};
|
||||
|
||||
use enums::by_table::debug_logs::debug_log_level::DebugLogLevel;
|
||||
use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
const DEFAULT_LIMIT: u32 = 50;
|
||||
const MAX_LIMIT: u32 = 200;
|
||||
|
||||
pub struct ListDebugLogsForUserArgs<'e, 'c, E>
|
||||
where
|
||||
E: 'e + Executor<'c, Database = MySql>,
|
||||
{
|
||||
pub user_token: &'e UserToken,
|
||||
|
||||
/// Cursor for pagination: only rows with `id` strictly below this are
|
||||
/// returned. Pass the `next_cursor` from a previous page.
|
||||
pub maybe_id_cursor: Option<u64>,
|
||||
|
||||
pub limit: Option<u32>,
|
||||
pub mysql_executor: E,
|
||||
pub phantom: PhantomData<&'c E>,
|
||||
}
|
||||
|
||||
pub struct ListDebugLogsForUserResult {
|
||||
pub debug_logs: Vec<UserDebugLogRow>,
|
||||
/// Cursor for the next page. `None` if there are no more results.
|
||||
pub next_cursor: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserDebugLogRow {
|
||||
pub id: u64,
|
||||
pub event_token: DebugLogEventToken,
|
||||
pub debug_log_type: DebugLogType,
|
||||
pub maybe_log_level: Option<DebugLogLevel>,
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub message: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RawUserDebugLogRow {
|
||||
id: u64,
|
||||
event_token: DebugLogEventToken,
|
||||
debug_log_type: DebugLogType,
|
||||
maybe_log_level: Option<DebugLogLevel>,
|
||||
maybe_creator_user_token: Option<UserToken>,
|
||||
message: String,
|
||||
created_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
/// List debug logs for a user, most recent first, cursor-paginated by `id`.
|
||||
pub async fn list_debug_logs_for_user<'e, 'c: 'e, E>(
|
||||
args: ListDebugLogsForUserArgs<'e, 'c, E>,
|
||||
) -> Result<ListDebugLogsForUserResult, sqlx::Error>
|
||||
where
|
||||
E: 'e + Executor<'c, Database = MySql>,
|
||||
{
|
||||
let limit = args.limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT);
|
||||
// Fetch limit + 1 so we can detect whether there's a next page.
|
||||
let fetch_limit = (limit + 1) as i64;
|
||||
|
||||
// Use u64::MAX when no cursor so all rows are included.
|
||||
let id_cursor = args.maybe_id_cursor.unwrap_or(u64::MAX);
|
||||
|
||||
let rows = sqlx::query_as!(
|
||||
RawUserDebugLogRow,
|
||||
r#"
|
||||
SELECT
|
||||
id as `id: u64`,
|
||||
event_token as `event_token: tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken`,
|
||||
debug_log_type as `debug_log_type: enums::by_table::debug_logs::debug_log_type::DebugLogType`,
|
||||
maybe_log_level as `maybe_log_level: enums::by_table::debug_logs::debug_log_level::DebugLogLevel`,
|
||||
maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
|
||||
message,
|
||||
created_at
|
||||
FROM debug_logs
|
||||
WHERE maybe_creator_user_token = ?
|
||||
AND id < ?
|
||||
ORDER BY id DESC
|
||||
LIMIT ?
|
||||
"#,
|
||||
args.user_token.as_str(),
|
||||
id_cursor,
|
||||
fetch_limit,
|
||||
)
|
||||
.fetch_all(args.mysql_executor)
|
||||
.await?;
|
||||
|
||||
let mut debug_logs: Vec<UserDebugLogRow> = rows.into_iter().map(|row| {
|
||||
UserDebugLogRow {
|
||||
id: row.id,
|
||||
event_token: row.event_token,
|
||||
debug_log_type: row.debug_log_type,
|
||||
maybe_log_level: row.maybe_log_level,
|
||||
maybe_creator_user_token: row.maybe_creator_user_token,
|
||||
message: row.message,
|
||||
created_at: row.created_at.and_utc(),
|
||||
}
|
||||
}).collect();
|
||||
|
||||
let next_cursor = if debug_logs.len() > limit as usize {
|
||||
debug_logs.truncate(limit as usize);
|
||||
debug_logs.last().map(|row| row.id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(ListDebugLogsForUserResult {
|
||||
debug_logs,
|
||||
next_cursor,
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
pub mod bulk_insert_debug_logs;
|
||||
pub mod insert_debug_log;
|
||||
pub mod list_all_debug_logs;
|
||||
pub mod list_debug_logs_for_token;
|
||||
pub mod list_debug_logs_for_user;
|
||||
@@ -0,0 +1,151 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::error::enum_error::EnumError;
|
||||
|
||||
/// Maximum serialized string length for database storage.
|
||||
pub const MAX_LENGTH: usize = 16;
|
||||
|
||||
/// Used in the `debug_logs` table in a `VARCHAR(16)` field (`maybe_log_level`).
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DebugLogLevel {
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Debug,
|
||||
Trace,
|
||||
}
|
||||
|
||||
impl_enum_display_and_debug_using_to_str!(DebugLogLevel);
|
||||
impl_mysql_enum_coders!(DebugLogLevel);
|
||||
impl_mysql_from_row!(DebugLogLevel);
|
||||
|
||||
impl DebugLogLevel {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Info => "info",
|
||||
Self::Warn => "warn",
|
||||
Self::Error => "error",
|
||||
Self::Debug => "debug",
|
||||
Self::Trace => "trace",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(value: &str) -> Result<Self, EnumError> {
|
||||
match value {
|
||||
"info" => Ok(Self::Info),
|
||||
"warn" => Ok(Self::Warn),
|
||||
"error" => Ok(Self::Error),
|
||||
"debug" => Ok(Self::Debug),
|
||||
"trace" => Ok(Self::Trace),
|
||||
_ => Err(EnumError::CouldNotConvertFromString(value.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
BTreeSet::from([
|
||||
Self::Info,
|
||||
Self::Warn,
|
||||
Self::Error,
|
||||
Self::Debug,
|
||||
Self::Trace,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::by_table::debug_logs::debug_log_level::DebugLogLevel;
|
||||
use crate::by_table::debug_logs::debug_log_level::MAX_LENGTH;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
use crate::error::enum_error::EnumError;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(DebugLogLevel::Info, "info");
|
||||
assert_serialization(DebugLogLevel::Warn, "warn");
|
||||
assert_serialization(DebugLogLevel::Error, "error");
|
||||
assert_serialization(DebugLogLevel::Debug, "debug");
|
||||
assert_serialization(DebugLogLevel::Trace, "trace");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(DebugLogLevel::Info.to_str(), "info");
|
||||
assert_eq!(DebugLogLevel::Warn.to_str(), "warn");
|
||||
assert_eq!(DebugLogLevel::Error.to_str(), "error");
|
||||
assert_eq!(DebugLogLevel::Debug.to_str(), "debug");
|
||||
assert_eq!(DebugLogLevel::Trace.to_str(), "trace");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(DebugLogLevel::from_str("info").unwrap(), DebugLogLevel::Info);
|
||||
assert_eq!(DebugLogLevel::from_str("warn").unwrap(), DebugLogLevel::Warn);
|
||||
assert_eq!(DebugLogLevel::from_str("error").unwrap(), DebugLogLevel::Error);
|
||||
assert_eq!(DebugLogLevel::from_str("debug").unwrap(), DebugLogLevel::Debug);
|
||||
assert_eq!(DebugLogLevel::from_str("trace").unwrap(), DebugLogLevel::Trace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str_err() {
|
||||
let result = DebugLogLevel::from_str("invalid");
|
||||
assert!(result.is_err());
|
||||
if let Err(EnumError::CouldNotConvertFromString(value)) = result {
|
||||
assert_eq!(value, "invalid");
|
||||
} else {
|
||||
panic!("Expected EnumError::CouldNotConvertFromString");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = DebugLogLevel::all_variants();
|
||||
assert_eq!(variants.len(), 5);
|
||||
assert_eq!(variants.pop_first(), Some(DebugLogLevel::Info));
|
||||
assert_eq!(variants.pop_first(), Some(DebugLogLevel::Warn));
|
||||
assert_eq!(variants.pop_first(), Some(DebugLogLevel::Error));
|
||||
assert_eq!(variants.pop_first(), Some(DebugLogLevel::Debug));
|
||||
assert_eq!(variants.pop_first(), Some(DebugLogLevel::Trace));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(DebugLogLevel::all_variants().len(), DebugLogLevel::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in DebugLogLevel::all_variants() {
|
||||
assert_eq!(variant, DebugLogLevel::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, DebugLogLevel::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, DebugLogLevel::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max_length() {
|
||||
for variant in DebugLogLevel::all_variants() {
|
||||
assert!(variant.to_str().len() <= MAX_LENGTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
pub mod debug_log_level;
|
||||
pub mod debug_log_type;
|
||||
@@ -88,7 +88,10 @@ use crate::http_server::endpoints::media_files::upsert_upload::write_error::Medi
|
||||
use crate::http_server::endpoints::media_files::upsert_upload::write_scene_file::write_scene_file_media_file_handler::*;
|
||||
use crate::http_server::endpoints::model_download::enqueue_gptsovits_model_download_handler::*;
|
||||
use artcraft_api_defs::generate::video::edit::beeble_switchx_edit_video::*;
|
||||
use crate::http_server::endpoints::moderation::debug_logs::moderation_list_debug_logs_for_token_handler::*;
|
||||
use artcraft_api_defs::moderation::debug_logs::debug_log_entry::*;
|
||||
use artcraft_api_defs::moderation::debug_logs::moderation_list_all_debug_logs::*;
|
||||
use artcraft_api_defs::moderation::debug_logs::moderation_list_debug_logs_for_token::*;
|
||||
use artcraft_api_defs::moderation::debug_logs::moderation_list_debug_logs_for_user::*;
|
||||
use crate::http_server::endpoints::moderation::jobs::moderation_get_job_by_token_handler::*;
|
||||
use crate::http_server::endpoints::moderation::user_feature_flags::moderator_edit_user_feature_flags_handler::*;
|
||||
use crate::http_server::endpoints::moderation::user_feature_flags::moderator_list_all_available_user_feature_flags_handler::*;
|
||||
@@ -602,7 +605,9 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
crate::http_server::endpoints::moderation::wallets::moderator_add_banked_balance_to_wallet_handler::moderator_add_banked_balance_to_wallet_handler,
|
||||
crate::http_server::endpoints::moderation::wallets::moderator_create_wallet_for_user_handler::moderator_create_wallet_for_user_handler,
|
||||
crate::http_server::endpoints::moderation::wallets::moderator_get_wallet_handler::moderator_get_wallet_handler,
|
||||
crate::http_server::endpoints::moderation::debug_logs::moderation_list_all_debug_logs_handler::moderation_list_all_debug_logs_handler,
|
||||
crate::http_server::endpoints::moderation::debug_logs::moderation_list_debug_logs_for_token_handler::moderation_list_debug_logs_for_token_handler,
|
||||
crate::http_server::endpoints::moderation::debug_logs::moderation_list_debug_logs_for_user_handler::moderation_list_debug_logs_for_user_handler,
|
||||
crate::http_server::endpoints::moderation::jobs::moderation_get_job_by_token_handler::moderation_get_job_by_token_handler,
|
||||
crate::http_server::endpoints::moderation::staff_audit_logs::moderator_list_staff_audit_logs_handler::moderator_list_staff_audit_logs_handler,
|
||||
crate::http_server::endpoints::moderation::user_referrals::moderator_list_global_user_referrals_handler::moderator_list_global_user_referrals_handler,
|
||||
@@ -1030,10 +1035,17 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
GetJobByTokenPathInfo,
|
||||
GetJobByTokenSuccessResponse,
|
||||
ModerationJobResponse,
|
||||
ListDebugLogsPathInfo,
|
||||
ListDebugLogsQueryParams,
|
||||
ListDebugLogsSuccessResponse,
|
||||
DebugLogEntry,
|
||||
ModerationDebugLogEntry,
|
||||
ModerationListAllDebugLogsEntry,
|
||||
ModerationListAllDebugLogsQueryParams,
|
||||
ModerationListAllDebugLogsSuccessResponse,
|
||||
ModerationListAllDebugLogsUser,
|
||||
ModerationListDebugLogsForTokenPathInfo,
|
||||
ModerationListDebugLogsForTokenQueryParams,
|
||||
ModerationListDebugLogsForTokenSuccessResponse,
|
||||
ModerationListDebugLogsForUserPathInfo,
|
||||
ModerationListDebugLogsForUserQueryParams,
|
||||
ModerationListDebugLogsForUserSuccessResponse,
|
||||
ListStaffAuditLogsQueryParams,
|
||||
ListStaffAuditLogsSuccessResponse,
|
||||
ListImpersonationRequestsQueryParams,
|
||||
|
||||
+2
@@ -1 +1,3 @@
|
||||
pub mod moderation_list_all_debug_logs_handler;
|
||||
pub mod moderation_list_debug_logs_for_token_handler;
|
||||
pub mod moderation_list_debug_logs_for_user_handler;
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::warn;
|
||||
|
||||
use artcraft_api_defs::moderation::debug_logs::moderation_list_all_debug_logs::{
|
||||
ModerationListAllDebugLogsEntry, ModerationListAllDebugLogsQueryParams,
|
||||
ModerationListAllDebugLogsSuccessResponse, ModerationListAllDebugLogsUser,
|
||||
};
|
||||
use mysql_queries::queries::debug_logs::list_all_debug_logs::{
|
||||
list_all_debug_logs, ListAllDebugLogsArgs,
|
||||
};
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::user_lookup::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/debug_logs/list_all",
|
||||
params(
|
||||
ModerationListAllDebugLogsQueryParams,
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ModerationListAllDebugLogsSuccessResponse),
|
||||
(status = 400, description = "Bad request (invalid severity)"),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
)]
|
||||
pub async fn moderation_list_all_debug_logs_handler(
|
||||
http_request: HttpRequest,
|
||||
query: web::Query<ModerationListAllDebugLogsQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModerationListAllDebugLogsSuccessResponse>, CommonWebError> {
|
||||
let maybe_log_levels = query.parsed_severities().map_err(|err| {
|
||||
CommonWebError::BadInputWithSimpleMessage(format!("invalid severities parameter: {}", err))
|
||||
})?;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &mut *mysql_connection).await?;
|
||||
|
||||
let result = list_all_debug_logs(ListAllDebugLogsArgs {
|
||||
maybe_log_levels,
|
||||
maybe_id_cursor: query.cursor,
|
||||
limit: query.limit,
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
}).await.map_err(|err| {
|
||||
warn!("Error listing all debug logs: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let debug_logs: Vec<ModerationListAllDebugLogsEntry> = result.debug_logs.into_iter().map(|row| {
|
||||
// The join only yields user fields when the creator user exists.
|
||||
let maybe_user = match (row.maybe_creator_user_token.clone(), row.maybe_user_display_name, row.maybe_user_username, row.maybe_user_gravatar_hash) {
|
||||
(Some(user_token), Some(display_name), Some(username), Some(gravatar_hash)) => {
|
||||
Some(ModerationListAllDebugLogsUser {
|
||||
user_token,
|
||||
display_name,
|
||||
username,
|
||||
gravatar_hash,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
ModerationListAllDebugLogsEntry {
|
||||
id: row.id,
|
||||
event_token: row.event_token,
|
||||
debug_log_type: row.debug_log_type,
|
||||
maybe_log_level: row.maybe_log_level,
|
||||
maybe_creator_user_token: row.maybe_creator_user_token,
|
||||
message: row.message,
|
||||
created_at: row.created_at,
|
||||
maybe_user,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
Ok(Json(ModerationListAllDebugLogsSuccessResponse {
|
||||
success: true,
|
||||
debug_logs,
|
||||
next_cursor: result.next_cursor,
|
||||
}))
|
||||
}
|
||||
+16
-50
@@ -2,80 +2,45 @@ use std::sync::Arc;
|
||||
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::warn;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
use artcraft_api_defs::moderation::debug_logs::debug_log_entry::ModerationDebugLogEntry;
|
||||
use artcraft_api_defs::moderation::debug_logs::moderation_list_debug_logs_for_token::{
|
||||
ModerationListDebugLogsForTokenPathInfo, ModerationListDebugLogsForTokenQueryParams,
|
||||
ModerationListDebugLogsForTokenSuccessResponse,
|
||||
};
|
||||
use mysql_queries::queries::debug_logs::list_debug_logs_for_token::{
|
||||
list_debug_logs_for_token, ListDebugLogsForTokenArgs,
|
||||
};
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::user_lookup::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
// ── Path params ──
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ListDebugLogsPathInfo {
|
||||
pub token: DebugLogEventToken,
|
||||
}
|
||||
|
||||
// ── Query params ──
|
||||
|
||||
#[derive(Deserialize, IntoParams, ToSchema)]
|
||||
pub struct ListDebugLogsQueryParams {
|
||||
pub limit: Option<u32>,
|
||||
}
|
||||
|
||||
// ── Response ──
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListDebugLogsSuccessResponse {
|
||||
pub success: bool,
|
||||
pub debug_logs: Vec<DebugLogEntry>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct DebugLogEntry {
|
||||
pub id: u64,
|
||||
pub event_token: DebugLogEventToken,
|
||||
pub debug_log_type: DebugLogType,
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub message: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
// ── Handler ──
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/debug_logs/list/{token}",
|
||||
params(
|
||||
("token" = String, Path, description = "Debug log event token"),
|
||||
ListDebugLogsQueryParams,
|
||||
ModerationListDebugLogsForTokenQueryParams,
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ListDebugLogsSuccessResponse),
|
||||
(status = 200, description = "Success", body = ModerationListDebugLogsForTokenSuccessResponse),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
)]
|
||||
pub async fn moderation_list_debug_logs_for_token_handler(
|
||||
http_request: HttpRequest,
|
||||
path: web::Path<ListDebugLogsPathInfo>,
|
||||
query: web::Query<ListDebugLogsQueryParams>,
|
||||
path: web::Path<ModerationListDebugLogsForTokenPathInfo>,
|
||||
query: web::Query<ModerationListDebugLogsForTokenQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListDebugLogsSuccessResponse>, CommonWebError> {
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
) -> Result<Json<ModerationListDebugLogsForTokenSuccessResponse>, CommonWebError> {
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &mut *mysql_connection).await?;
|
||||
|
||||
let rows = list_debug_logs_for_token(ListDebugLogsForTokenArgs {
|
||||
event_token: &path.token,
|
||||
limit: query.limit,
|
||||
@@ -86,18 +51,19 @@ pub async fn moderation_list_debug_logs_for_token_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let debug_logs: Vec<DebugLogEntry> = rows.into_iter().map(|row| {
|
||||
DebugLogEntry {
|
||||
let debug_logs: Vec<ModerationDebugLogEntry> = rows.into_iter().map(|row| {
|
||||
ModerationDebugLogEntry {
|
||||
id: row.id,
|
||||
event_token: row.event_token,
|
||||
debug_log_type: row.debug_log_type,
|
||||
maybe_log_level: row.maybe_log_level,
|
||||
maybe_creator_user_token: row.maybe_creator_user_token,
|
||||
message: row.message,
|
||||
created_at: row.created_at,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
Ok(Json(ListDebugLogsSuccessResponse {
|
||||
Ok(Json(ModerationListDebugLogsForTokenSuccessResponse {
|
||||
success: true,
|
||||
debug_logs,
|
||||
}))
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::warn;
|
||||
|
||||
use artcraft_api_defs::moderation::debug_logs::debug_log_entry::ModerationDebugLogEntry;
|
||||
use artcraft_api_defs::moderation::debug_logs::moderation_list_debug_logs_for_user::{
|
||||
ModerationListDebugLogsForUserPathInfo, ModerationListDebugLogsForUserQueryParams,
|
||||
ModerationListDebugLogsForUserSuccessResponse,
|
||||
};
|
||||
use mysql_queries::queries::debug_logs::list_debug_logs_for_user::{
|
||||
list_debug_logs_for_user, ListDebugLogsForUserArgs,
|
||||
};
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::user_lookup::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/debug_logs/user_list/{user_token}",
|
||||
params(
|
||||
("user_token" = String, Path, description = "User token"),
|
||||
ModerationListDebugLogsForUserQueryParams,
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ModerationListDebugLogsForUserSuccessResponse),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
)]
|
||||
pub async fn moderation_list_debug_logs_for_user_handler(
|
||||
http_request: HttpRequest,
|
||||
path: web::Path<ModerationListDebugLogsForUserPathInfo>,
|
||||
query: web::Query<ModerationListDebugLogsForUserQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModerationListDebugLogsForUserSuccessResponse>, CommonWebError> {
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &mut *mysql_connection).await?;
|
||||
|
||||
let result = list_debug_logs_for_user(ListDebugLogsForUserArgs {
|
||||
user_token: &path.user_token,
|
||||
maybe_id_cursor: query.cursor,
|
||||
limit: query.limit,
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
}).await.map_err(|err| {
|
||||
warn!("Error listing debug logs for user {}: {:?}", path.user_token, err);
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let debug_logs: Vec<ModerationDebugLogEntry> = result.debug_logs.into_iter().map(|row| {
|
||||
ModerationDebugLogEntry {
|
||||
id: row.id,
|
||||
event_token: row.event_token,
|
||||
debug_log_type: row.debug_log_type,
|
||||
maybe_log_level: row.maybe_log_level,
|
||||
maybe_creator_user_token: row.maybe_creator_user_token,
|
||||
message: row.message,
|
||||
created_at: row.created_at,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
Ok(Json(ModerationListDebugLogsForUserSuccessResponse {
|
||||
success: true,
|
||||
debug_logs,
|
||||
next_cursor: result.next_cursor,
|
||||
}))
|
||||
}
|
||||
+2
@@ -138,6 +138,7 @@ pub async fn omni_api_image_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::HttpRequest,
|
||||
maybe_log_level: None,
|
||||
message: &serde_json::to_string(&*request).unwrap_or_default(),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
@@ -169,6 +170,7 @@ pub async fn omni_api_image_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::FalRequest,
|
||||
maybe_log_level: None,
|
||||
message: &format!("{:#?}", outbound_request),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
|
||||
+3
@@ -205,6 +205,7 @@ pub async fn omni_api_video_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::HttpRequest,
|
||||
maybe_log_level: None,
|
||||
message: &serde_json::to_string(&*request).unwrap_or_default(),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
@@ -236,6 +237,7 @@ pub async fn omni_api_video_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::FalRequest,
|
||||
maybe_log_level: None,
|
||||
message: &format!("{:#?}", outbound_request),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
@@ -253,6 +255,7 @@ pub async fn omni_api_video_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::GrokApiRequest,
|
||||
maybe_log_level: None,
|
||||
message: &format!("{:#?}", outbound_request),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
|
||||
+2
@@ -134,6 +134,7 @@ pub async fn omni_gen_image_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::HttpRequest,
|
||||
maybe_log_level: None,
|
||||
message: &serde_json::to_string(&*request).unwrap_or_default(),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
@@ -165,6 +166,7 @@ pub async fn omni_gen_image_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::FalRequest,
|
||||
maybe_log_level: None,
|
||||
message: &format!("{:#?}", outbound_request),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
|
||||
+3
@@ -195,6 +195,7 @@ pub async fn omni_gen_video_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::HttpRequest,
|
||||
maybe_log_level: None,
|
||||
message: &serde_json::to_string(&*request).unwrap_or_default(),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
@@ -226,6 +227,7 @@ pub async fn omni_gen_video_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::FalRequest,
|
||||
maybe_log_level: None,
|
||||
message: &format!("{:#?}", outbound_request),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
@@ -243,6 +245,7 @@ pub async fn omni_gen_video_generate_handler(
|
||||
apriori_debug_log_event_token: Some(&debug_log_event_token),
|
||||
maybe_creator_user_token: Some(user_token),
|
||||
debug_log_type: DebugLogType::GrokApiRequest,
|
||||
maybe_log_level: None,
|
||||
message: &format!("{:#?}", outbound_request),
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
|
||||
+1
@@ -101,6 +101,7 @@ pub async fn beeble_webhook_handler(
|
||||
apriori_debug_log_event_token: Some(debug_log_event_token),
|
||||
maybe_creator_user_token: job.maybe_creator_user_token.as_ref(),
|
||||
debug_log_type: DebugLogType::BeebleWebhook,
|
||||
maybe_log_level: None,
|
||||
message: &raw_body,
|
||||
mysql_executor: &mut *mysql_connection,
|
||||
phantom: Default::default(),
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ pub async fn handle_failed_fal_webhook(
|
||||
apriori_debug_log_event_token: Some(debug_log_event_token),
|
||||
maybe_creator_user_token: job.maybe_creator_user_token.as_ref(),
|
||||
debug_log_type: DebugLogType::FalWebhook,
|
||||
maybe_log_level: None,
|
||||
message: raw_body,
|
||||
mysql_executor: &mut **mysql_connection,
|
||||
phantom: Default::default(),
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ pub async fn handle_successful_fal_webhook(
|
||||
apriori_debug_log_event_token: Some(debug_log_event_token),
|
||||
maybe_creator_user_token: job.maybe_creator_user_token.as_ref(),
|
||||
debug_log_type: DebugLogType::FalWebhook,
|
||||
maybe_log_level: None,
|
||||
message: raw_body,
|
||||
mysql_executor: &mut **mysql_connection,
|
||||
phantom: Default::default(),
|
||||
|
||||
+10
@@ -36,7 +36,9 @@ use crate::http_server::endpoints::moderation::user::moderator_user_lookup_by_st
|
||||
use crate::http_server::endpoints::moderation::user::moderator_user_lookup_handler::moderator_user_lookup_handler;
|
||||
use crate::http_server::endpoints::moderation::alerts::moderation_send_alert_handler::moderation_send_alert_handler;
|
||||
use crate::http_server::endpoints::moderation::staff_audit_logs::moderator_list_staff_audit_logs_handler::moderator_list_staff_audit_logs_handler;
|
||||
use crate::http_server::endpoints::moderation::debug_logs::moderation_list_all_debug_logs_handler::moderation_list_all_debug_logs_handler;
|
||||
use crate::http_server::endpoints::moderation::debug_logs::moderation_list_debug_logs_for_token_handler::moderation_list_debug_logs_for_token_handler;
|
||||
use crate::http_server::endpoints::moderation::debug_logs::moderation_list_debug_logs_for_user_handler::moderation_list_debug_logs_for_user_handler;
|
||||
use crate::http_server::endpoints::moderation::jobs::moderation_get_job_by_token_handler::moderation_get_job_by_token_handler;
|
||||
use crate::http_server::endpoints::moderation::user_feature_flags::moderator_edit_user_feature_flags_handler::moderator_edit_user_feature_flags_handler;
|
||||
use crate::http_server::endpoints::moderation::user_feature_flags::moderator_list_all_available_user_feature_flags_handler::moderator_list_all_available_user_feature_flags_handler;
|
||||
@@ -102,6 +104,14 @@ pub fn add_moderator_routes<T, B> (app: App<T>) -> App<T>
|
||||
.route(web::get().to(moderation_list_debug_logs_for_token_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/list_all")
|
||||
.route(web::get().to(moderation_list_all_debug_logs_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/user_list/{user_token}")
|
||||
.route(web::get().to(moderation_list_debug_logs_for_user_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
.service(
|
||||
web::scope("/user_emails")
|
||||
|
||||
Reference in new issue
Block a user