[Experimental] add an option for brainlet instance owners to require a special code sign up
This commit is contained in:
parent
6d142767b0
commit
bc75e3f24c
4 changed files with 94 additions and 12 deletions
|
@ -120,7 +120,7 @@ app.get('/category/:category/info', [
|
||||||
users: users
|
users: users
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}));
|
}, undefined, config.roleMap.USER));
|
||||||
|
|
||||||
app.get('/category/list', authenticateEndpoint(async (req, res, user) => {
|
app.get('/category/list', authenticateEndpoint(async (req, res, user) => {
|
||||||
let count = parseInt(req.query.count);
|
let count = parseInt(req.query.count);
|
||||||
|
@ -136,6 +136,6 @@ app.get('/category/list', authenticateEndpoint(async (req, res, user) => {
|
||||||
message: 'SUCCESS_CATEGORY_LIST_FETCHED',
|
message: 'SUCCESS_CATEGORY_LIST_FETCHED',
|
||||||
categories
|
categories
|
||||||
});
|
});
|
||||||
}));
|
}, undefined, config.roleMap.USER));
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
|
@ -22,11 +22,26 @@ const createAccountLimiter = rateLimit({
|
||||||
message: "You are being rate limited"
|
message: "You are being rate limited"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/account/create/info', async (req, res) => {
|
||||||
|
const restrictions = config.restrictions.signup;
|
||||||
|
let requiresCode = false;
|
||||||
|
if (restrictions && restrictions.specialCode) {
|
||||||
|
requiresCode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
error: false,
|
||||||
|
message: 'SUCCESS_ACCOUNT_CREATE_INFO_FETCH',
|
||||||
|
requiresSpecialCode: requiresCode
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/account/create', [
|
app.post('/account/create', [
|
||||||
createAccountLimiter,
|
createAccountLimiter,
|
||||||
body('username').not().isEmpty().trim().isLength({ min: 3, max: 32 }).isAlphanumeric(),
|
body('username').not().isEmpty().trim().isLength({ min: 3, max: 32 }).isAlphanumeric(),
|
||||||
body('email').not().isEmpty().isEmail().normalizeEmail(),
|
body('email').not().isEmpty().isEmail().normalizeEmail(),
|
||||||
body('password').not().isEmpty().isLength({ min: 8, max: 128 })
|
body('password').not().isEmpty().isLength({ min: 8, max: 128 }),
|
||||||
|
body('specialCode').optional().isLength({ min: 12, max: 12 }).isAlphanumeric()
|
||||||
], async (req, res) => {
|
], async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const errors = validationResult(req);
|
const errors = validationResult(req);
|
||||||
|
@ -35,6 +50,22 @@ app.post('/account/create', [
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const restrictions = config.restrictions.signup;
|
||||||
|
if (restrictions && restrictions.specialCode) {
|
||||||
|
const passedSpecialCode = req.body.specialCode;
|
||||||
|
const specialCode = restrictions.specialCode;
|
||||||
|
|
||||||
|
if (passedSpecialCode && specialCode) {
|
||||||
|
if (specialCode !== passedSpecialCode) {
|
||||||
|
res.status(401).json({ error: true, message: 'ERROR_REQUEST_SPECIAL_CODE_MISSING', errors: [{ msg: 'No specialCode passed', param: 'specialCode', location: 'body' }] });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(401).json({ error: true, message: 'ERROR_REQUEST_SPECIAL_CODE_MISSING', errors: [{ msg: 'No specialCode passed', param: 'specialCode', location: 'body' }] });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const username = req.body.username;
|
const username = req.body.username;
|
||||||
|
|
||||||
const existingUser = await User.findByUsername(username);
|
const existingUser = await User.findByUsername(username);
|
||||||
|
@ -171,7 +202,7 @@ app.get('/user/:userid/info', [
|
||||||
message: 'SUCCESS_USER_DATA_FETCHED',
|
message: 'SUCCESS_USER_DATA_FETCHED',
|
||||||
user: await otherUser.getPublicObject(),
|
user: await otherUser.getPublicObject(),
|
||||||
});
|
});
|
||||||
}));
|
}, undefined, config.roleMap.USER));
|
||||||
|
|
||||||
app.post('/browser/token/clear', authenticateEndpoint((req, res, user) => {
|
app.post('/browser/token/clear', authenticateEndpoint((req, res, user) => {
|
||||||
res.clearCookie('token');
|
res.clearCookie('token');
|
||||||
|
|
|
@ -47,13 +47,26 @@
|
||||||
</md-field>
|
</md-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="mode === 'SPECIAL_CODE'">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
The owner of this Brainlet instance has made it so that signing up requires a special code.
|
||||||
|
</p>
|
||||||
|
<md-field>
|
||||||
|
<label>Special code</label>
|
||||||
|
<md-input v-model="specialCodeInput" type="password"></md-input>
|
||||||
|
</md-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</md-card-content>
|
</md-card-content>
|
||||||
|
|
||||||
<md-card-actions>
|
<md-card-actions>
|
||||||
<md-button v-if="mode === 'SIGNUP'" @click="mode='LOGIN'" class="md-dense">Log in instead</md-button>
|
<md-button v-if="mode === 'SIGNUP'" @click="mode='LOGIN'" class="md-dense">Log in instead</md-button>
|
||||||
<md-button v-if="mode === 'LOGIN'" @click="mode='SIGNUP'" class="md-dense">Sign up instead</md-button>
|
<md-button v-if="mode === 'LOGIN'" @click="mode='SIGNUP'" class="md-dense">Sign up instead</md-button>
|
||||||
|
|
||||||
<md-button v-if="mode === 'SIGNUP'" class="md-dense md-raised md-primary" @click="performAccountCreation()">Sign up</md-button>
|
<md-button v-if="mode === 'SPECIAL_CODE'" class="md-dense" @click="mode='SIGNUP'">Go back</md-button>
|
||||||
|
<md-button v-if="mode === 'SIGNUP' || mode === 'SPECIAL_CODE'" class="md-dense md-raised md-primary" @click="performAccountCreation()">Sign up</md-button>
|
||||||
|
|
||||||
<md-button v-if="mode === 'LOGIN'" class="md-dense md-raised md-primary" @click="performTokenCreation()">Log in</md-button>
|
<md-button v-if="mode === 'LOGIN'" class="md-dense md-raised md-primary" @click="performTokenCreation()">Log in</md-button>
|
||||||
|
|
||||||
<md-button v-if="mode === 'MANAGE'" class="md-dense md-raised" @click="performTokenRemoval()">Log out</md-button>
|
<md-button v-if="mode === 'MANAGE'" class="md-dense md-raised" @click="performTokenRemoval()">Log out</md-button>
|
||||||
|
|
|
@ -30,6 +30,9 @@ const getSignupMessageFromError = (json) => {
|
||||||
case 'email': {
|
case 'email': {
|
||||||
return 'Invalid email.';
|
return 'Invalid email.';
|
||||||
}
|
}
|
||||||
|
case 'specialCode': {
|
||||||
|
return 'Invalid special code.';
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return 'Invalid value sent to server. Something went wrong.';
|
return 'Invalid value sent to server. Something went wrong.';
|
||||||
|
@ -58,13 +61,15 @@ const app = new Vue({
|
||||||
usernameInput: '',
|
usernameInput: '',
|
||||||
emailInput: '',
|
emailInput: '',
|
||||||
passwordInput: '',
|
passwordInput: '',
|
||||||
|
specialCodeInput: '',
|
||||||
mode: 'SIGNUP',
|
mode: 'SIGNUP',
|
||||||
showSnackbarNotification: false,
|
showSnackbarNotification: false,
|
||||||
snackbarNotification: '',
|
snackbarNotification: '',
|
||||||
snackbarNotificationDuration: 999999,
|
snackbarNotificationDuration: 999999,
|
||||||
snackbarButtonText: 'Ok',
|
snackbarButtonText: 'Ok',
|
||||||
successfulLogin: false,
|
successfulLogin: false,
|
||||||
loggedInUser: {}
|
loggedInUser: {},
|
||||||
|
requiresSpecialCode: null
|
||||||
},
|
},
|
||||||
mounted: async function() {
|
mounted: async function() {
|
||||||
const res = await fetch(`${window.location.origin}/api/v1/users/current/info`, {
|
const res = await fetch(`${window.location.origin}/api/v1/users/current/info`, {
|
||||||
|
@ -86,7 +91,21 @@ const app = new Vue({
|
||||||
this.successfulLogin = true;
|
this.successfulLogin = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.mode = 'SIGNUP';
|
const resInfo = await fetch(`${window.location.origin}/api/v1/users/account/create/info`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resInfo.ok) {
|
||||||
|
const json = await resInfo.json();
|
||||||
|
this.requiresSpecialCode = json.requiresSpecialCode;
|
||||||
|
|
||||||
|
this.mode = 'SIGNUP';
|
||||||
|
} else {
|
||||||
|
this.mode = '_ERROR';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -104,6 +123,9 @@ const app = new Vue({
|
||||||
case 'MANAGE': {
|
case 'MANAGE': {
|
||||||
return this.loggedInUser.username || 'Unknown account';
|
return this.loggedInUser.username || 'Unknown account';
|
||||||
}
|
}
|
||||||
|
case 'SPECIAL_CODE': {
|
||||||
|
return 'Just one more step'
|
||||||
|
}
|
||||||
case 'NONE': {
|
case 'NONE': {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -192,17 +214,33 @@ const app = new Vue({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
performAccountCreation: async function() {
|
performAccountCreation: async function() {
|
||||||
|
let jsonData = {
|
||||||
|
username: this.usernameInput,
|
||||||
|
email: this.emailInput,
|
||||||
|
password: this.passwordInput
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.requiresSpecialCode) {
|
||||||
|
if (this.mode === 'SIGNUP') {
|
||||||
|
this.mode = 'SPECIAL_CODE';
|
||||||
|
return;
|
||||||
|
} else if (this.mode !== 'SPECIAL_CODE') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData = {
|
||||||
|
specialCode: this.specialCodeInput,
|
||||||
|
...jsonData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetch(`${window.location.origin}/api/v1/users/account/create`, {
|
const res = await fetch(`${window.location.origin}/api/v1/users/account/create`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(jsonData)
|
||||||
username: this.usernameInput,
|
|
||||||
email: this.emailInput,
|
|
||||||
password: this.passwordInput
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
|
|
Reference in a new issue