mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-06-15 09:09:10 +00:00
feat: add audit logging via webhook (#309)
* feat: add audit logging via webhook * addd missing auditLogWebhook values in various places
This commit is contained in:
parent
5671a408c1
commit
acc604f83f
16 changed files with 488 additions and 22 deletions
|
@ -1,14 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import { Space as SpaceComponent } from './Space';
|
||||
import { LinedSpace, Space } from './Space';
|
||||
|
||||
export default {
|
||||
title: 'Atoms',
|
||||
title: 'Atoms/Space',
|
||||
};
|
||||
|
||||
export const Space = () => (
|
||||
export const space = () => (
|
||||
<>
|
||||
hello world
|
||||
<SpaceComponent />
|
||||
<Space />
|
||||
but im over here
|
||||
</>
|
||||
);
|
||||
|
||||
export const linedSpace = () => (
|
||||
<>
|
||||
hello world
|
||||
<LinedSpace />
|
||||
but im over here
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
import styled from 'styled-components';
|
||||
import { palette } from '@roleypoly/design-system/atoms/colors';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
export const Space = styled.div`
|
||||
height: 15px;
|
||||
`;
|
||||
|
||||
export const LinedSpace = styled.div<{ width?: number }>`
|
||||
height: 7.5px;
|
||||
margin-top: 7.5px;
|
||||
border-top: 1px solid ${palette.taupe300};
|
||||
${(props) =>
|
||||
props.width &&
|
||||
css`
|
||||
width: ${props.width}px;
|
||||
`}
|
||||
`;
|
||||
|
|
|
@ -174,6 +174,7 @@ export const guildData: GuildData = {
|
|||
message: 'henlo worl!!',
|
||||
categories: [mockCategory, mockCategorySingle],
|
||||
features: Features.None,
|
||||
auditLogWebhook: null,
|
||||
};
|
||||
|
||||
export const user: DiscordUser = {
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import { palette } from '@roleypoly/design-system/atoms/colors';
|
||||
import { FaderOpacity } from '@roleypoly/design-system/atoms/fader';
|
||||
import { TextInput } from '@roleypoly/design-system/atoms/text-input';
|
||||
import { AmbientLarge, Text } from '@roleypoly/design-system/atoms/typography';
|
||||
import { MessageBox } from '@roleypoly/design-system/organisms/role-picker/RolePicker.styled';
|
||||
import { GuildData, WebhookValidationStatus } from '@roleypoly/types';
|
||||
import { GoAlert, GoInfo } from 'react-icons/go';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import styled from 'styled-components';
|
||||
|
||||
type Props = {
|
||||
onChange: (guildData: GuildData) => void;
|
||||
guildData: GuildData;
|
||||
validationStatus: WebhookValidationStatus | null;
|
||||
};
|
||||
|
||||
export const ServerUtilities = (props: Props) => {
|
||||
return (
|
||||
<MessageBox>
|
||||
<Text>
|
||||
(optional) Webhook URL for Audit Logging{' '}
|
||||
<GoInfo
|
||||
data-for="server-utilities"
|
||||
data-tip="Reports changes made in the editor to a Webhook integration within your Discord server."
|
||||
/>
|
||||
</Text>
|
||||
<TextInput
|
||||
placeholder="https://discord.com/api/webhooks/000000000000000000/..."
|
||||
value={props.guildData.auditLogWebhook || ''}
|
||||
onChange={(event) =>
|
||||
props.onChange({ ...props.guildData, auditLogWebhook: event.target.value })
|
||||
}
|
||||
/>
|
||||
<FaderOpacity isVisible={props.validationStatus !== WebhookValidationStatus.Ok}>
|
||||
<ValidationStatus validationStatus={props.validationStatus} />
|
||||
</FaderOpacity>
|
||||
<ReactTooltip id="server-utilities" />
|
||||
</MessageBox>
|
||||
);
|
||||
};
|
||||
|
||||
const ValidationStatus = (props: Pick<Props, 'validationStatus'>) => {
|
||||
switch (props.validationStatus) {
|
||||
case WebhookValidationStatus.NotDiscordURL:
|
||||
return (
|
||||
<AmbientLarge>
|
||||
<Alert /> URL must be to a Discord webhook, starting with
|
||||
"https://discord.com/api/webhooks/".
|
||||
</AmbientLarge>
|
||||
);
|
||||
case WebhookValidationStatus.NotSameGuild:
|
||||
return (
|
||||
<AmbientLarge>
|
||||
<Alert /> Webhook must be in the same guild you are currently editing.
|
||||
</AmbientLarge>
|
||||
);
|
||||
case WebhookValidationStatus.DoesNotExist:
|
||||
return (
|
||||
<AmbientLarge>
|
||||
<Alert /> This webhook doesn't exist.
|
||||
</AmbientLarge>
|
||||
);
|
||||
default:
|
||||
return <AmbientLarge> </AmbientLarge>;
|
||||
}
|
||||
};
|
||||
|
||||
const Alert = styled(GoAlert)`
|
||||
color: ${palette.red400};
|
||||
position: relative;
|
||||
top: 2px;
|
||||
`;
|
|
@ -1,10 +1,16 @@
|
|||
import { Space } from '@roleypoly/design-system/atoms/space';
|
||||
import { LinedSpace, Space } from '@roleypoly/design-system/atoms/space';
|
||||
import { EditableServerMessage } from '@roleypoly/design-system/molecules/editable-server-message';
|
||||
import { ServerMasthead } from '@roleypoly/design-system/molecules/server-masthead';
|
||||
import { ServerUtilities } from '@roleypoly/design-system/molecules/server-utilities/ServerUtilities';
|
||||
import { SecondaryEditing } from '@roleypoly/design-system/organisms/masthead';
|
||||
import { Container } from '@roleypoly/design-system/organisms/role-picker/RolePicker.styled';
|
||||
import { ServerCategoryEditor } from '@roleypoly/design-system/organisms/server-category-editor';
|
||||
import { Category, PresentableGuild } from '@roleypoly/types';
|
||||
import {
|
||||
Category,
|
||||
GuildData,
|
||||
PresentableGuild,
|
||||
WebhookValidationStatus,
|
||||
} from '@roleypoly/types';
|
||||
import deepEqual from 'deep-equal';
|
||||
import React from 'react';
|
||||
|
||||
|
@ -13,17 +19,26 @@ export type EditorShellProps = {
|
|||
onGuildChange?: (guild: PresentableGuild) => void;
|
||||
onCategoryChange?: (category: Category) => void;
|
||||
onMessageChange?: (message: PresentableGuild['data']['message']) => void;
|
||||
errors: {
|
||||
webhookValidation: WebhookValidationStatus;
|
||||
};
|
||||
};
|
||||
|
||||
export const EditorShell = (props: EditorShellProps) => {
|
||||
const [guild, setGuild] = React.useState<PresentableGuild>(props.guild);
|
||||
const [errors, setErrors] = React.useState<EditorShellProps['errors']>(props.errors);
|
||||
|
||||
React.useEffect(() => {
|
||||
setGuild(props.guild);
|
||||
}, [props.guild]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setErrors(props.errors);
|
||||
}, [props.errors]);
|
||||
|
||||
const reset = () => {
|
||||
setGuild(props.guild);
|
||||
setErrors({ webhookValidation: WebhookValidationStatus.Ok });
|
||||
};
|
||||
|
||||
const replaceCategories = (categories: Category[]) => {
|
||||
|
@ -38,6 +53,12 @@ export const EditorShell = (props: EditorShellProps) => {
|
|||
});
|
||||
};
|
||||
|
||||
const updateGuildData = (data: PresentableGuild['data']) => {
|
||||
setGuild((currentGuild) => {
|
||||
return { ...currentGuild, data };
|
||||
});
|
||||
};
|
||||
|
||||
const doSubmit = () => {
|
||||
props.onGuildChange?.(guild);
|
||||
};
|
||||
|
@ -65,7 +86,48 @@ export const EditorShell = (props: EditorShellProps) => {
|
|||
/>
|
||||
<Space />
|
||||
<ServerCategoryEditor guild={guild} onChange={replaceCategories} />
|
||||
<LinedSpace />
|
||||
<ServerUtilities
|
||||
guildData={guild.data}
|
||||
onChange={updateGuildData}
|
||||
validationStatus={validateWebhook(
|
||||
guild.data.auditLogWebhook,
|
||||
errors.webhookValidation
|
||||
)}
|
||||
/>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const validateWebhook = (
|
||||
webhook: GuildData['auditLogWebhook'],
|
||||
validationStatus: WebhookValidationStatus
|
||||
) => {
|
||||
if (!webhook) {
|
||||
return WebhookValidationStatus.NoneSet;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(webhook);
|
||||
|
||||
if (
|
||||
url.hostname !== 'discord.com' ||
|
||||
url.protocol !== 'https:' ||
|
||||
url.pathname.startsWith('api/webhooks/')
|
||||
) {
|
||||
return WebhookValidationStatus.NotDiscordURL;
|
||||
}
|
||||
} catch (e) {
|
||||
return WebhookValidationStatus.Ok;
|
||||
}
|
||||
|
||||
if (
|
||||
validationStatus !== WebhookValidationStatus.Ok &&
|
||||
validationStatus !== WebhookValidationStatus.NoneSet
|
||||
) {
|
||||
return validationStatus;
|
||||
}
|
||||
|
||||
return WebhookValidationStatus.Ok;
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ export default {
|
|||
),
|
||||
],
|
||||
args: {
|
||||
errors: { validationStatus: 0 },
|
||||
guilds: mastheadSlugs,
|
||||
user: user,
|
||||
guild: guildEnum.guilds[0],
|
||||
|
|
|
@ -11,7 +11,9 @@ export const EditorTemplate = (
|
|||
props;
|
||||
return (
|
||||
<AppShell {...appShellProps} activeGuildId={guild.id} small double>
|
||||
<EditorShell guild={guild} onGuildChange={onGuildChange} />
|
||||
<EditorShell guild={guild} onGuildChange={onGuildChange} errors={props.errors} />
|
||||
</AppShell>
|
||||
);
|
||||
};
|
||||
|
||||
export type EditorErrors = EditorShellProps['errors'];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue