mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 01:38:44 +00:00
list and add IP address bans
This commit is contained in:
1 parent
18b8569292
commit
cbd8303e88
5 files changed
+257
-1
No files matched your search
@@ -198,6 +198,18 @@ class ApiConfig {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/events`;
|
||||
}
|
||||
|
||||
getModerationIpBanList(): string {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/moderation/ip_bans/list`;
|
||||
}
|
||||
|
||||
createModerationIpBan(): string {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/moderation/ip_bans/add`;
|
||||
}
|
||||
|
||||
deleteModerationIpBan(ipAddress: string): string {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/moderation/ip_bans/${ipAddress}/delete`;
|
||||
}
|
||||
|
||||
private getScheme() : string {
|
||||
return this.isLocalDev ? "http" : "https";
|
||||
}
|
||||
|
||||
@@ -7,9 +7,19 @@ interface Props {
|
||||
}
|
||||
|
||||
function NewFooterNavFc(props: Props) {
|
||||
|
||||
let moderationLink = <span />;
|
||||
|
||||
if (props.sessionWrapper.canBanUsers()) {
|
||||
moderationLink = (
|
||||
<Link to="/moderation">Mod Controls</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<hr />
|
||||
{moderationLink}
|
||||
<p>
|
||||
We're trying to build a democratized <a href="https://the.storyteller.company">future of creativity, filmmaking and storytelling</a>.
|
||||
Contact <Link to="/profile/echelon">@echelon</Link> for more information.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { FirehoseEventListFc } from './firehose/FirehoseEventListFc';
|
||||
import { LoginFc } from './login/LoginFc';
|
||||
import { ModerationFc } from './moderation/ModerationFc';
|
||||
import { ModerationIpBanListFc } from './moderation_ip_ban_list/ModerationIpBanListFc';
|
||||
import { NewFooterNavFc } from './NewFooterNavFc';
|
||||
import { NewTopNavFc } from './NewTopNavFc';
|
||||
import { ProfileEditFc } from './profile_edit/ProfileEditFc';
|
||||
@@ -15,7 +17,6 @@ import { TtsModelUploadJob } from '../jobs/TtsModelUploadJobs';
|
||||
import { TtsModelViewFc } from './tts_model_view/TtsModelViewFc';
|
||||
import { TtsResultViewFc } from './tts_result_view/TtsResultViewFc';
|
||||
import { UploadChoiceFc } from './upload/UploadChoiceFc';
|
||||
import { UploadComponent } from './upload/UploadComponent';
|
||||
import { UploadTtsModelFc } from './upload/UploadTtsModelFc';
|
||||
import { UploadW2lPhotoFc } from './upload/UploadW2lPhotoFc';
|
||||
import { UploadW2lVideoFc } from './upload/UploadW2lVideoFc';
|
||||
@@ -176,6 +177,18 @@ class NewVocodesContainer extends React.Component<Props, State> {
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/ip_bans">
|
||||
<ModerationIpBanListFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation">
|
||||
<ModerationFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/">
|
||||
<TtsModelFormFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { SessionWrapper } from '../../session/SessionWrapper';
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
}
|
||||
|
||||
function ModerationFc(props: Props) {
|
||||
|
||||
if (!props.sessionWrapper.canBanUsers()) {
|
||||
return <h1>Unauthorized</h1>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title is-1"> Moderation Controls </h1>
|
||||
|
||||
<Link
|
||||
to="/moderation/ip_bans"
|
||||
className="button is-link is-large is-fullwidth is-outlined"
|
||||
>IP Ban List</Link>
|
||||
|
||||
<br />
|
||||
<p>More mod controls will be added in the future: user roles, activity tracking, timed bans, account bans, etc.</p>
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { ModerationFc };
|
||||
@@ -0,0 +1,189 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ApiConfig } from '../../common/ApiConfig';
|
||||
import { SessionWrapper } from '../../session/SessionWrapper';
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
}
|
||||
|
||||
interface IpBanListResponse {
|
||||
success: boolean,
|
||||
ip_address_bans: Array<IpBanListItem>,
|
||||
}
|
||||
|
||||
interface IpBanListItem {
|
||||
ip_address: string,
|
||||
maybe_target_user_token: string,
|
||||
maybe_target_username: string,
|
||||
|
||||
mod_user_token: string,
|
||||
mod_notes: string,
|
||||
|
||||
created_at: string,
|
||||
updated_at: string,
|
||||
}
|
||||
|
||||
function ModerationIpBanListFc(props: Props) {
|
||||
const [ipBanList, setIpBanList] = useState<Array<IpBanListItem>>([]);
|
||||
|
||||
// Form
|
||||
const [newIpAddress, setNewIpAddress] = useState<string>("");
|
||||
const [modNotes, setModNotes] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
const api = new ApiConfig();
|
||||
const endpointUrl = api.getModerationIpBanList();
|
||||
|
||||
fetch(endpointUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
const response : IpBanListResponse = res;
|
||||
if (!response.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
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<HTMLInputElement>) => {
|
||||
setNewIpAddress((ev.target as HTMLInputElement).value)
|
||||
};
|
||||
|
||||
const handleModNotesChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
setModNotes((ev.target as HTMLInputElement).value)
|
||||
};
|
||||
|
||||
const handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) : boolean => {
|
||||
ev.preventDefault();
|
||||
|
||||
const api = new ApiConfig();
|
||||
const endpointUrl = api.createModerationIpBan();
|
||||
|
||||
const request = {
|
||||
ip_address: newIpAddress,
|
||||
mod_notes: modNotes,
|
||||
}
|
||||
|
||||
fetch(endpointUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(request),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
history.go(0); // NB: Force reload
|
||||
}
|
||||
})
|
||||
.catch(e => {});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!props.sessionWrapper.canBanUsers()) {
|
||||
return <h1>Unauthorized</h1>;
|
||||
}
|
||||
|
||||
let rows : Array<JSX.Element> = [];
|
||||
|
||||
ipBanList.forEach(ban => {
|
||||
|
||||
rows.push(
|
||||
<tr key={ban.ip_address}>
|
||||
<td>{ban.ip_address}</td>
|
||||
<td>{ban.mod_notes}</td>
|
||||
<td>{ban.created_at}</td>
|
||||
</tr>
|
||||
)
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title is-1"> Moderation Ip Ban List </h1>
|
||||
|
||||
<p>IP Address bans will prevent bad actors from using and abusing the website.</p>
|
||||
<br />
|
||||
|
||||
<h3 className="title is-3"> Create Ban </h3>
|
||||
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<div className="field">
|
||||
<label className="label">Ip Address</label>
|
||||
<div className="control has-icons-left has-icons-right">
|
||||
<input
|
||||
onChange={handleNewIpAddressChange}
|
||||
className="input"
|
||||
type="text"
|
||||
placeholder="IP Address, eg. 255.255.255.255"
|
||||
value={newIpAddress}
|
||||
/>
|
||||
<span className="icon is-small is-left">
|
||||
<i className="fas fa-envelope"></i>
|
||||
</span>
|
||||
<span className="icon is-small is-right">
|
||||
<i className="fas fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
</div>
|
||||
{/*<p className="help">{invalidReason}</p>*/}
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">Moderator Notes</label>
|
||||
<div className="control has-icons-left has-icons-right">
|
||||
<input
|
||||
onChange={handleModNotesChange}
|
||||
className="input"
|
||||
type="text"
|
||||
placeholder="Notes / reason for ban"
|
||||
value={modNotes}
|
||||
/>
|
||||
<span className="icon is-small is-left">
|
||||
<i className="fas fa-envelope"></i>
|
||||
</span>
|
||||
<span className="icon is-small is-right">
|
||||
<i className="fas fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
</div>
|
||||
{/*<p className="help">{invalidReason}</p>*/}
|
||||
</div>
|
||||
|
||||
<button className="button is-danger is-large is-fullwidth">Create Ban</button>
|
||||
|
||||
</form>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<h3 className="title is-3"> Existing Bans </h3>
|
||||
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IP Address</th>
|
||||
<th>Moderator Notes</th>
|
||||
<th>Created At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { ModerationIpBanListFc };
|
||||
Reference in new issue
Block a user