Skip to content

Commit 5d2409c

Browse files
committed
Add ban URLs from for admin
1 parent dd1501d commit 5d2409c

File tree

5 files changed

+213
-66
lines changed

5 files changed

+213
-66
lines changed

client/actions/index.js

Lines changed: 3 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,3 @@
1-
import {
2-
createShortUrl,
3-
getUrlsList,
4-
deleteShortUrl,
5-
showShortenerLoading,
6-
setShortenerFormError,
7-
} from './url';
8-
9-
import {
10-
setDomain,
11-
setApiKey,
12-
showDomainInput,
13-
getUserSettings,
14-
setCustomDomain,
15-
deleteCustomDomain,
16-
generateApiKey,
17-
} from './settings';
18-
19-
import {
20-
showPageLoading,
21-
hidePageLoading,
22-
authUser,
23-
unauthUser,
24-
sentVerification,
25-
showAuthError,
26-
showLoginLoading,
27-
showSignupLoading,
28-
authRenew,
29-
signupUser,
30-
loginUser,
31-
logoutUser,
32-
renewAuthUser,
33-
} from './auth';
34-
35-
export {
36-
createShortUrl,
37-
getUrlsList,
38-
deleteShortUrl,
39-
showShortenerLoading,
40-
setShortenerFormError,
41-
setDomain,
42-
setApiKey,
43-
showDomainInput,
44-
getUserSettings,
45-
setCustomDomain,
46-
deleteCustomDomain,
47-
generateApiKey,
48-
showPageLoading,
49-
hidePageLoading,
50-
authUser,
51-
unauthUser,
52-
sentVerification,
53-
showAuthError,
54-
showLoginLoading,
55-
showSignupLoading,
56-
authRenew,
57-
signupUser,
58-
loginUser,
59-
logoutUser,
60-
renewAuthUser,
61-
};
1+
export * from './url';
2+
export * from './settings';
3+
export * from './auth';

client/actions/settings.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import {
88
SET_DOMAIN,
99
SET_APIKEY,
1010
SHOW_DOMAIN_INPUT,
11+
BAN_URL,
1112
} from './actionTypes';
1213

1314
const deleteDomain = () => ({ type: DELETE_DOMAIN });
1415
const setDomainError = payload => ({ type: DOMAIN_ERROR, payload });
1516
const showDomainLoading = () => ({ type: DOMAIN_LOADING });
1617
const showApiLoading = () => ({ type: API_LOADING });
18+
const urlBanned = () => ({ type: BAN_URL });
1719

1820
export const setDomain = payload => ({ type: SET_DOMAIN, payload });
1921
export const setApiKey = payload => ({ type: SET_APIKEY, payload });
@@ -65,3 +67,15 @@ export const generateApiKey = () => async dispatch => {
6567
//
6668
}
6769
};
70+
71+
export const banUrl = params => async dispatch => {
72+
try {
73+
const { data } = await axios.post('/api/url/admin/ban', params, {
74+
headers: { Authorization: cookie.get('token') },
75+
});
76+
dispatch(urlBanned());
77+
return data.message;
78+
} catch ({ response }) {
79+
return Promise.reject(response.data && response.data.error);
80+
}
81+
};

client/components/Checkbox/Checkbox.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ const Wrapper = styled.div`
66
display: flex;
77
justify-content: flex-start;
88
align-items: center;
9-
margin: 24px 16px 24px;
9+
margin: 16px 0 16px;
10+
11+
${({ withMargin }) =>
12+
withMargin &&
13+
css`
14+
margin: 24px 16px 24px;
15+
`};
1016
1117
:first-child {
1218
margin-left: 0;
@@ -77,8 +83,8 @@ const Box = styled.span`
7783
`};
7884
`;
7985

80-
const Checkbox = ({ checked, label, id, onClick }) => (
81-
<Wrapper>
86+
const Checkbox = ({ checked, label, id, withMargin, onClick }) => (
87+
<Wrapper withMargin={withMargin}>
8288
<Box checked={checked} id={id} onClick={onClick}>
8389
{label}
8490
</Box>
@@ -87,12 +93,14 @@ const Checkbox = ({ checked, label, id, onClick }) => (
8793

8894
Checkbox.propTypes = {
8995
checked: PropTypes.bool,
96+
withMargin: PropTypes.bool,
9097
label: PropTypes.string.isRequired,
9198
id: PropTypes.string.isRequired,
9299
onClick: PropTypes.func,
93100
};
94101

95102
Checkbox.defaultProps = {
103+
withMargin: true,
96104
checked: false,
97105
onClick: f => f,
98106
};

client/components/Settings/Settings.js

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Component } from 'react';
1+
import React, { Component, Fragment } from 'react';
22
import PropTypes from 'prop-types';
33
import Router from 'next/router';
44
import { bindActionCreators } from 'redux';
@@ -9,6 +9,7 @@ import axios from 'axios';
99
import SettingsWelcome from './SettingsWelcome';
1010
import SettingsDomain from './SettingsDomain';
1111
import SettingsPassword from './SettingsPassword';
12+
import SettingsBan from './SettingsBan';
1213
import SettingsApi from './SettingsApi';
1314
import Modal from '../Modal';
1415
import { fadeIn } from '../../helpers/animations';
@@ -18,6 +19,7 @@ import {
1819
getUserSettings,
1920
setCustomDomain,
2021
showDomainInput,
22+
banUrl,
2123
} from '../../actions';
2224

2325
const Wrapper = styled.div`
@@ -76,7 +78,17 @@ class Settings extends Component {
7678
showModal: false,
7779
passwordMessage: '',
7880
passwordError: '',
81+
ban: {
82+
domain: false,
83+
error: '',
84+
host: false,
85+
loading: false,
86+
message: '',
87+
user: false,
88+
},
7989
};
90+
this.onSubmitBan = this.onSubmitBan.bind(this);
91+
this.onChangeBanCheckboxes = this.onChangeBanCheckboxes.bind(this);
8092
this.handleCustomDomain = this.handleCustomDomain.bind(this);
8193
this.deleteDomain = this.deleteDomain.bind(this);
8294
this.showModal = this.showModal.bind(this);
@@ -89,6 +101,64 @@ class Settings extends Component {
89101
this.props.getUserSettings();
90102
}
91103

104+
async onSubmitBan(e) {
105+
e.preventDefault();
106+
const { ban: { domain, host, user } } = this.state;
107+
this.setState(state => ({
108+
ban: {
109+
...state.ban,
110+
loading: true,
111+
},
112+
}));
113+
const id = e.currentTarget.elements.id.value;
114+
let message;
115+
let error;
116+
try {
117+
message = await this.props.banUrl({
118+
id,
119+
domain,
120+
host,
121+
user,
122+
});
123+
} catch (err) {
124+
error = err;
125+
}
126+
this.setState(
127+
state => ({
128+
ban: {
129+
...state.ban,
130+
loading: false,
131+
message,
132+
error,
133+
},
134+
}),
135+
() => {
136+
setTimeout(() => {
137+
this.setState(state => ({
138+
ban: {
139+
...state.ban,
140+
loading: false,
141+
message: '',
142+
error: '',
143+
},
144+
}));
145+
}, 2000);
146+
}
147+
);
148+
}
149+
150+
onChangeBanCheckboxes(type) {
151+
return e => {
152+
const { checked } = e.target;
153+
this.setState(state => ({
154+
ban: {
155+
...state.ban,
156+
[type]: !checked,
157+
},
158+
}));
159+
};
160+
}
161+
92162
handleCustomDomain(e) {
93163
e.preventDefault();
94164
if (this.props.domainLoading) return null;
@@ -148,9 +218,21 @@ class Settings extends Component {
148218
}
149219

150220
render() {
221+
const { auth: { user, admin } } = this.props;
151222
return (
152223
<Wrapper>
153-
<SettingsWelcome user={this.props.auth.user} />
224+
<SettingsWelcome user={user} />
225+
<hr />
226+
{admin && (
227+
<Fragment>
228+
<SettingsBan
229+
{...this.state.ban}
230+
onSubmitBan={this.onSubmitBan}
231+
onChangeBanCheckboxes={this.onChangeBanCheckboxes}
232+
/>
233+
<hr />
234+
</Fragment>
235+
)}
154236
<SettingsDomain
155237
handleCustomDomain={this.handleCustomDomain}
156238
loading={this.props.domainLoading}
@@ -180,12 +262,14 @@ class Settings extends Component {
180262

181263
Settings.propTypes = {
182264
auth: PropTypes.shape({
265+
admin: PropTypes.bool.isRequired,
183266
isAuthenticated: PropTypes.bool.isRequired,
184267
user: PropTypes.string.isRequired,
185268
}).isRequired,
186269
apiLoading: PropTypes.bool,
187270
deleteCustomDomain: PropTypes.func.isRequired,
188271
domainLoading: PropTypes.bool,
272+
banUrl: PropTypes.func.isRequired,
189273
setCustomDomain: PropTypes.func.isRequired,
190274
generateApiKey: PropTypes.func.isRequired,
191275
getUserSettings: PropTypes.func.isRequired,
@@ -214,6 +298,7 @@ const mapStateToProps = ({
214298
});
215299

216300
const mapDispatchToProps = dispatch => ({
301+
banUrl: bindActionCreators(banUrl, dispatch),
217302
deleteCustomDomain: bindActionCreators(deleteCustomDomain, dispatch),
218303
setCustomDomain: bindActionCreators(setCustomDomain, dispatch),
219304
generateApiKey: bindActionCreators(generateApiKey, dispatch),
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import styled from 'styled-components';
4+
import TextInput from '../TextInput';
5+
import Button from '../Button';
6+
import Checkbox from '../Checkbox';
7+
8+
const Form = styled.form`
9+
position: relative;
10+
display: flex;
11+
flex-direction: column;
12+
margin: 32px 0;
13+
14+
input {
15+
flex: 0 0 auto;
16+
margin-right: 16px;
17+
}
18+
`;
19+
20+
const InputWrapper = styled.div`
21+
display: flex;
22+
`;
23+
24+
const Message = styled.div`
25+
position: absolute;
26+
left: 0;
27+
bottom: -32px;
28+
color: green;
29+
`;
30+
31+
const Error = styled.div`
32+
position: absolute;
33+
left: 0;
34+
bottom: -32px;
35+
color: red;
36+
`;
37+
38+
const SettingsBan = props => (
39+
<div>
40+
<h3>Ban link</h3>
41+
<Form onSubmit={props.onSubmitBan}>
42+
<InputWrapper>
43+
<Message>{props.message}</Message>
44+
<TextInput
45+
id="id"
46+
name="id"
47+
type="text"
48+
placeholder="Link ID (e.g. K7b2A)"
49+
height={44}
50+
small
51+
/>
52+
<Button type="submit" icon={props.loading ? 'loader' : 'lock'} disabled={props.loading}>
53+
{props.loading ? 'Baning...' : 'Ban'}
54+
</Button>
55+
</InputWrapper>
56+
<div>
57+
<Checkbox
58+
id="user"
59+
name="user"
60+
label="Ban user (and all of their links)"
61+
withMargin={false}
62+
checked={props.user}
63+
onClick={props.onChangeBanCheckboxes('user')}
64+
/>
65+
<Checkbox
66+
id="domain"
67+
name="domain"
68+
label="Ban domain"
69+
withMargin={false}
70+
checked={props.domain}
71+
onClick={props.onChangeBanCheckboxes('domain')}
72+
/>
73+
<Checkbox
74+
id="host"
75+
name="host"
76+
label="Ban Host/IP"
77+
withMargin={false}
78+
checked={props.host}
79+
onClick={props.onChangeBanCheckboxes('host')}
80+
/>
81+
</div>
82+
<Error>{props.error}</Error>
83+
</Form>
84+
</div>
85+
);
86+
87+
SettingsBan.propTypes = {
88+
domain: PropTypes.bool.isRequired,
89+
error: PropTypes.string.isRequired,
90+
host: PropTypes.bool.isRequired,
91+
loading: PropTypes.bool.isRequired,
92+
message: PropTypes.string.isRequired,
93+
onChangeBanCheckboxes: PropTypes.func.isRequired,
94+
onSubmitBan: PropTypes.func.isRequired,
95+
user: PropTypes.bool.isRequired,
96+
};
97+
98+
export default SettingsBan;

0 commit comments

Comments
 (0)