diff --git a/packages/fakeyou/src/v2/view/moderation/moderation_ip_ban_list/ModerationIpBanListFc.tsx b/packages/fakeyou/src/v2/view/moderation/moderation_ip_ban_list/ModerationIpBanListFc.tsx index bd9db7f438..58830460e0 100644 --- a/packages/fakeyou/src/v2/view/moderation/moderation_ip_ban_list/ModerationIpBanListFc.tsx +++ b/packages/fakeyou/src/v2/view/moderation/moderation_ip_ban_list/ModerationIpBanListFc.tsx @@ -1,32 +1,32 @@ -import React, { useEffect, useState } from 'react'; -import { Link, useHistory } from 'react-router-dom'; -import { ApiConfig } from '@storyteller/components'; -import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper'; -import { formatDistance } from 'date-fns'; -import { BackLink } from '../../_common/BackLink'; -import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig'; +import React, { useEffect, useState } from "react"; +import { Link, useHistory } from "react-router-dom"; +import { ApiConfig } from "@storyteller/components"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { formatDistance } from "date-fns"; +import { BackLink } from "../../_common/BackLink"; +import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig"; interface Props { - sessionWrapper: SessionWrapper, + sessionWrapper: SessionWrapper; } interface IpBanListResponse { - success: boolean, - ip_address_bans: Array, + success: boolean; + ip_address_bans: Array; } interface IpBanListItem { - ip_address: string, - maybe_target_user_token: string, - maybe_target_username: string, + ip_address: string; + maybe_target_user_token: string; + maybe_target_username: string; - mod_user_token: string, - mod_username: string, - mod_display_name: string, - mod_notes: string, + mod_user_token: string; + mod_username: string; + mod_display_name: string; + mod_notes: string; - created_at: string, - updated_at: string, + created_at: string; + updated_at: string; } function ModerationIpBanListFc(props: Props) { @@ -43,35 +43,35 @@ function ModerationIpBanListFc(props: Props) { const endpointUrl = api.getModerationIpBanList(); fetch(endpointUrl, { - method: 'GET', + method: "GET", headers: { - 'Accept': 'application/json', + Accept: "application/json", }, - credentials: 'include', + credentials: "include", }) - .then(res => res.json()) - .then(res => { - const response : IpBanListResponse = res; - if (!response.success) { - return; - } + .then((res) => res.json()) + .then((res) => { + const response: IpBanListResponse = res; + if (!response.success) { + return; + } - setIpBanList(response.ip_address_bans) - }) - .catch(e => { - //this.props.onSpeakErrorCallback(); - }); + setIpBanList(response.ip_address_bans); + }) + .catch((e) => { + //this.props.onSpeakErrorCallback(); + }); }, []); // NB: Empty array dependency sets to run ONLY on mount const handleNewIpAddressChange = (ev: React.FormEvent) => { - setNewIpAddress((ev.target as HTMLInputElement).value) + setNewIpAddress((ev.target as HTMLInputElement).value); }; const handleModNotesChange = (ev: React.FormEvent) => { - setModNotes((ev.target as HTMLInputElement).value) + setModNotes((ev.target as HTMLInputElement).value); }; - const handleFormSubmit = (ev: React.FormEvent) : boolean => { + const handleFormSubmit = (ev: React.FormEvent): boolean => { ev.preventDefault(); const api = new ApiConfig(); @@ -80,41 +80,43 @@ function ModerationIpBanListFc(props: Props) { const request = { ip_address: newIpAddress, mod_notes: modNotes, - } + }; fetch(endpointUrl, { - method: 'POST', + method: "POST", headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', + Accept: "application/json", + "Content-Type": "application/json", }, - credentials: 'include', + credentials: "include", body: JSON.stringify(request), }) - .then(res => res.json()) - .then(res => { - if (res.success) { - history.go(0); // NB: Force reload - } - }) - .catch(e => {}); + .then((res) => res.json()) + .then((res) => { + if (res.success) { + history.go(0); // NB: Force reload + } + }) + .catch((e) => {}); return false; - } + }; if (!props.sessionWrapper.canBanUsers()) { return

Unauthorized

; } const now = new Date(); - let rows : Array = []; + let rows: Array = []; - ipBanList.forEach(ban => { + ipBanList.forEach((ban) => { const modUserLink = `/profile/${ban.mod_username}`; const viewBanLink = `/moderation/ip_bans/${ban.ip_address}`; const createTime = new Date(ban.created_at); - const relativeCreateTime = formatDistance(createTime, now, { addSuffix: true }); + const relativeCreateTime = formatDistance(createTime, now, { + addSuffix: true, + }); rows.push( @@ -125,94 +127,104 @@ function ModerationIpBanListFc(props: Props) { {ban.mod_notes} {relativeCreateTime} - view / edit + + View/Edit + - ) + ); }); return (
-

Moderation Ip Ban List

- -

- -

- -
- -

IP Address bans will prevent bad actors from using and abusing the website.

- -
- -

Create Ban

+
+

Moderation IP Ban List

+

+ IP Address bans will prevent bad actors from using and abusing the + website. +

+
+ +
+
-
- -
- - - - - - - +
+
+

Create Ban

+
+
+
+ +
+ + + + + + + +
+ {/*

{invalidReason}

*/} +
+
+ +
+ + + + + + + +
+ {/*

{invalidReason}

*/} +
+
+
- {/*

{invalidReason}

*/}
- -
- -
- - - - - - - -
- {/*

{invalidReason}

*/} +
+
- - - -
-
- -

Existing Bans

- - - - - - - - - - - - - {rows} - -
IP AddressModeratorModerator NotesCreated AtView / Edit
+
+
+

Existing Bans

+
+ + + + + + + + + + + {rows} +
IP AddressModeratorModerator NotesCreated AtView / Edit
+
+
+
- ) + ); } export { ModerationIpBanListFc }; diff --git a/packages/fakeyou/src/v2/view/moderation/moderation_job_stats/ModerationJobStatsFc.tsx b/packages/fakeyou/src/v2/view/moderation/moderation_job_stats/ModerationJobStatsFc.tsx index 147e9c5bfc..ff75fb9b1c 100644 --- a/packages/fakeyou/src/v2/view/moderation/moderation_job_stats/ModerationJobStatsFc.tsx +++ b/packages/fakeyou/src/v2/view/moderation/moderation_job_stats/ModerationJobStatsFc.tsx @@ -1,25 +1,39 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper'; -import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig'; -import { BackLink } from '../../_common/BackLink'; -import { GetTtsInferenceStats, GetTtsInferenceStatsIsOk } from '../../../api/moderation/GetTtsInferenceStats'; -import { GetW2lInferenceStats, GetW2lInferenceStatsIsOk } from '../../../api/moderation/GetW2lInferenceStats'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faBomb, faRedo } from '@fortawesome/free-solid-svg-icons'; -import { KillAction, KillTtsInferenceJobsIsSuccess, KillTtsInferenceJobs } from '@storyteller/components/src/api/moderation/tts/KillTtsInferenceJobs'; +import React, { useCallback, useEffect, useState } from "react"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig"; +import { BackLink } from "../../_common/BackLink"; +import { + GetTtsInferenceStats, + GetTtsInferenceStatsIsOk, +} from "../../../api/moderation/GetTtsInferenceStats"; +import { + GetW2lInferenceStats, + GetW2lInferenceStatsIsOk, +} from "../../../api/moderation/GetW2lInferenceStats"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faBomb, faRedo } from "@fortawesome/free-solid-svg-icons"; +import { + KillAction, + KillTtsInferenceJobsIsSuccess, + KillTtsInferenceJobs, +} from "@storyteller/components/src/api/moderation/tts/KillTtsInferenceJobs"; interface Props { - sessionWrapper: SessionWrapper, + sessionWrapper: SessionWrapper; } function ModerationJobStatsFc(props: Props) { // NB: We have more TTS stats than W2L stats now. const [ttsSecondsSinceFirst, setTtsSecondsSinceFirst] = useState(-1); const [ttsPendingJobCount, setTtsPendingJobCount] = useState(-1); - const [ttsPendingPriorityNonzeroJobCount, setTtsPendingPriorityNonzeroJobCount] = useState(-1); - const [ttsPendingPriorityGtOneJobCount, setTtsPendingPriorityGtOneJobCount] = useState(-1); - const [ttsAttemptFailedJobCount, setTtsAttemptFailedJobCount] = useState(-1); - + const [ + ttsPendingPriorityNonzeroJobCount, + setTtsPendingPriorityNonzeroJobCount, + ] = useState(-1); + const [ttsPendingPriorityGtOneJobCount, setTtsPendingPriorityGtOneJobCount] = + useState(-1); + const [ttsAttemptFailedJobCount, setTtsAttemptFailedJobCount] = + useState(-1); const [w2lPendingJobCount, setW2lPendingJobCount] = useState(-1); const [w2lSecondsSinceFirst, setW2lSecondsSinceFirst] = useState(-1); @@ -29,12 +43,16 @@ function ModerationJobStatsFc(props: Props) { if (GetTtsInferenceStatsIsOk(response)) { setTtsSecondsSinceFirst(response.seconds_since_first); setTtsPendingJobCount(response.pending_count); - setTtsPendingPriorityNonzeroJobCount(response.pending_priority_nonzero_count); - setTtsPendingPriorityGtOneJobCount(response.pending_priority_gt_one_count); + setTtsPendingPriorityNonzeroJobCount( + response.pending_priority_nonzero_count + ); + setTtsPendingPriorityGtOneJobCount( + response.pending_priority_gt_one_count + ); setTtsAttemptFailedJobCount(response.attempt_failed_count); } }, []); - + const getW2lStats = useCallback(async () => { const response = await GetW2lInferenceStats(); if (GetW2lInferenceStatsIsOk(response)) { @@ -54,26 +72,26 @@ function ModerationJobStatsFc(props: Props) { const killPending = async () => { await doKill(KillAction.AllPending); - } + }; const killPendingAndFailed = async () => { await doKill(KillAction.AllPendingAndFailed); - } + }; const killZeroPriorityPending = async () => { await doKill(KillAction.ZeroPriorityPending); - } + }; - const doKill = async(killAction: KillAction) => { + const doKill = async (killAction: KillAction) => { const response = await KillTtsInferenceJobs(killAction); if (KillTtsInferenceJobsIsSuccess(response)) { reloadStats(); } - } + }; if (ttsPendingJobCount === -1 && w2lPendingJobCount === -1) { - return
+ return
; } if (!props.sessionWrapper.canBanUsers()) { @@ -85,148 +103,161 @@ function ModerationJobStatsFc(props: Props) { return (
-

Job Stats

+
+

Job Stats

+
+ +
+
- +
+
+

TTS Stats

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Misc Stats +
TTS wait time{ttsWait}
+ Failed Jobs by Type +
Pending Jobs (All){ttsPendingJobCount} pending
Pending Jobs (Priority > 0){ttsPendingPriorityNonzeroJobCount} pending
Pending Jobs (Priority > 1){ttsPendingPriorityGtOneJobCount} pending
Failed Jobs w/ Retry{ttsAttemptFailedJobCount} pending
+
+
+
-
-
- -

TTS Stats

+
+
+

W2L Stats

+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Misc Stats +
W2L wait time{w2lWait}
+ Failed Jobs by Type +
Pending Jobs (All){w2lPendingJobCount} pending
+
+
+
- - - - - - - - - - - - -
Misc Stats
- TTS wait time - {ttsWait}
+
+
+

Reload Job Stats

+
+ +
+
+
- - - - - - - - - - - - - - - - - - - - - - - - -
Failed Jobs by Type
- Pending Jobs (All) - {ttsPendingJobCount} pending
- Pending Jobs (Priority > 0) - {ttsPendingPriorityNonzeroJobCount} pending
- Pending Jobs (Priority > 1) - {ttsPendingPriorityGtOneJobCount} pending
- Failed Jobs w/ Retry - {ttsAttemptFailedJobCount} pending
+
+
+

Kill Stuck Jobs (Danger Zone)

+
+
(Please don't do this.)
+
+ -
+ -

W2L Stats

+ +
+
+
+
- - - - - - - - - - - - -
Misc Stats
- W2L wait time - {w2lWait}
- - - - - - - - - - - - - - -
Failed Jobs by Type
- Pending Jobs (All) - {w2lPendingJobCount} pending
- -
- -

Reload Job Stats

- - - -
-
- -

Kill Stuck Jobs (Danger Zone)

-

(Please don't do this.)

- - - -
- - - -
- - - - -
-
- - +
+ +
- ) + ); } -function humanWaitTime(seconds: number) : string { +function humanWaitTime(seconds: number): string { if (seconds === -1) { - return 'error'; + return "error"; } else if (seconds < 60) { return `${seconds} seconds`; } else if (seconds < 60 * 60) { diff --git a/packages/fakeyou/src/v2/view/moderation/moderation_main/ModerationFc.tsx b/packages/fakeyou/src/v2/view/moderation/moderation_main/ModerationFc.tsx index 4c96932306..b4b0b2e660 100644 --- a/packages/fakeyou/src/v2/view/moderation/moderation_main/ModerationFc.tsx +++ b/packages/fakeyou/src/v2/view/moderation/moderation_main/ModerationFc.tsx @@ -1,74 +1,104 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig'; -import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper'; +import React from "react"; +import { Link } from "react-router-dom"; +import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faBan, + faClipboardCheck, + faListCheck, + faMicrophone, + faTags, + faUsers, +} from "@fortawesome/free-solid-svg-icons"; interface Props { - sessionWrapper: SessionWrapper, + sessionWrapper: SessionWrapper; } function ModerationFc(props: Props) { - if (!props.sessionWrapper.canBanUsers()) { return

Unauthorized

; } return (
-

Moderation Controls

+
+

+ Moderation Controls +

+
-

Emergency

- IP Bans - -
+
+
+

Emergency

+
+
+ + + IP Bans + + + + Job Stats + +
+
+
+
- Job Stats - -
+
+
+

Lookup, Stats, and Editing

+
+
+ + + User List + -

Lookup, Stats, and Editing

+ + + Manage TTS Categories + - User List + + + Unapproved W2L Templates + -
- - Manage TTS Categories - -
- - Unapproved W2L Templates - -
- - Voice Stats - -
- -

- More mod controls will be added in the future: user roles, activity tracking, - timed bans, account bans, etc. -

+ + + Voice Stats + +
+
+
+
+
+

+ More mod controls will be added in the future: user roles, activity + tracking, timed bans, account bans, etc. +

+
- ) + ); } export { ModerationFc };