Started work on migration UI. Improved email handling.
This commit is contained in:
parent
bddbd9cd36
commit
eb48b92551
31
src/controllers/migrateController.js
Normal file
31
src/controllers/migrateController.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
|
//Config
|
||||||
|
const config = require('../../config.json');
|
||||||
|
|
||||||
|
//Local Imports
|
||||||
|
const altchaUtils = require('../utils/altchaUtils');
|
||||||
|
const csrfUtils = require('../utils/csrfUtils');
|
||||||
|
|
||||||
|
//register page functions
|
||||||
|
module.exports.get = async function(req, res){
|
||||||
|
//Generate captcha
|
||||||
|
const challenge = await altchaUtils.genCaptcha();
|
||||||
|
|
||||||
|
//Render page
|
||||||
|
return res.render('migrate', {instance: config.instanceName, user: req.session.user, challenge, csrfToken: csrfUtils.generateToken(req)});
|
||||||
|
}
|
||||||
30
src/routers/migrateRouter.js
Normal file
30
src/routers/migrateRouter.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
|
//npm imports
|
||||||
|
const { Router } = require('express');
|
||||||
|
|
||||||
|
|
||||||
|
//local imports
|
||||||
|
const migrateController = require("../controllers/migrateController");
|
||||||
|
|
||||||
|
//globals
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
//routing functions
|
||||||
|
router.get('/', migrateController.get);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
@ -316,6 +316,12 @@ migrationSchema.statics.consumeByUsername = async function(ip, migration){
|
||||||
//Pull migration doc by case-insensitive username
|
//Pull migration doc by case-insensitive username
|
||||||
const migrationDB = await this.findOne({user: new RegExp(migration.user, 'i')});
|
const migrationDB = await this.findOne({user: new RegExp(migration.user, 'i')});
|
||||||
|
|
||||||
|
//If we have no migration document
|
||||||
|
if(migrationDB == null){
|
||||||
|
//Bitch and moan
|
||||||
|
throw loggerUtils.exceptionSmith("Incorrect username/password.", "migration");
|
||||||
|
}
|
||||||
|
|
||||||
//Wait on the miration DB token to be consumed
|
//Wait on the miration DB token to be consumed
|
||||||
await migrationDB.consume(ip, migration);
|
await migrationDB.consume(ip, migration);
|
||||||
}
|
}
|
||||||
|
|
@ -359,7 +365,7 @@ migrationSchema.methods.consume = async function(ip, migration){
|
||||||
await newUser.tattooIPRecord(ip);
|
await newUser.tattooIPRecord(ip);
|
||||||
|
|
||||||
//if we submitted an email
|
//if we submitted an email
|
||||||
if(this.email != null && this.email != ''){
|
if(this.email != null && validator.isEmail(this.email)){
|
||||||
//Generate new email change request
|
//Generate new email change request
|
||||||
const requestDB = await emailChangeModel.create({user: newUser._id, newEmail: this.email, ipHash: ip});
|
const requestDB = await emailChangeModel.create({user: newUser._id, newEmail: this.email, ipHash: ip});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ const channelRouter = require('./routers/channelRouter');
|
||||||
const newChannelRouter = require('./routers/newChannelRouter');
|
const newChannelRouter = require('./routers/newChannelRouter');
|
||||||
const passwordResetRouter = require('./routers/passwordResetRouter');
|
const passwordResetRouter = require('./routers/passwordResetRouter');
|
||||||
const emailChangeRouter = require('./routers/emailChangeController');
|
const emailChangeRouter = require('./routers/emailChangeController');
|
||||||
|
const migrateRouter = require('./routers/migrateRouter');
|
||||||
//Panel
|
//Panel
|
||||||
const panelRouter = require('./routers/panelRouter');
|
const panelRouter = require('./routers/panelRouter');
|
||||||
//Popup
|
//Popup
|
||||||
|
|
@ -161,6 +162,7 @@ app.use('/c', channelRouter);
|
||||||
app.use('/newChannel', newChannelRouter);
|
app.use('/newChannel', newChannelRouter);
|
||||||
app.use('/passwordReset', passwordResetRouter);
|
app.use('/passwordReset', passwordResetRouter);
|
||||||
app.use('/emailChange', emailChangeRouter);
|
app.use('/emailChange', emailChangeRouter);
|
||||||
|
app.use('/migrate', migrateRouter);
|
||||||
//Panel
|
//Panel
|
||||||
app.use('/panel', panelRouter);
|
app.use('/panel', panelRouter);
|
||||||
//tooltip
|
//tooltip
|
||||||
|
|
|
||||||
|
|
@ -173,10 +173,10 @@ module.exports.errorMiddleware = function(err, req, res, next){
|
||||||
* @param {Error} err - error to dump to file
|
* @param {Error} err - error to dump to file
|
||||||
* @param {Date} date - Date of error, defaults to now
|
* @param {Date} date - Date of error, defaults to now
|
||||||
*/
|
*/
|
||||||
module.exports.dumpError = async function(err, date = new Date()){
|
module.exports.dumpError = async function(err, date = new Date(), subDir){
|
||||||
try{
|
try{
|
||||||
//Crash directory
|
//Crash directory
|
||||||
const dir = "./log/crash/"
|
const dir = `./log/crash/${subDir}`
|
||||||
|
|
||||||
//Double check crash folder exists
|
//Double check crash folder exists
|
||||||
try{
|
try{
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,11 @@ const config = require('../../config.json');
|
||||||
|
|
||||||
//NPM imports
|
//NPM imports
|
||||||
const nodeMailer = require("nodemailer");
|
const nodeMailer = require("nodemailer");
|
||||||
|
const validator = require('validator');
|
||||||
|
|
||||||
|
//local imports
|
||||||
|
const loggerUtils = require('./loggerUtils');
|
||||||
|
|
||||||
|
|
||||||
//Setup mail transport
|
//Setup mail transport
|
||||||
/**
|
/**
|
||||||
|
|
@ -43,28 +48,38 @@ const transporter = nodeMailer.createTransport({
|
||||||
* @returns {Object} Sent mail info
|
* @returns {Object} Sent mail info
|
||||||
*/
|
*/
|
||||||
module.exports.mailem = async function(to, subject, body, htmlBody = false){
|
module.exports.mailem = async function(to, subject, body, htmlBody = false){
|
||||||
//Create mail object
|
try{
|
||||||
const mailObj = {
|
//If we have a bad email address
|
||||||
from: `"Tokebot🤖💨"<${config.mail.address}>`,
|
if(!validator.isEmail(to)){
|
||||||
to,
|
//fuck off
|
||||||
subject
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
//If we're sending HTML
|
//Create mail object
|
||||||
if(htmlBody){
|
const mailObj = {
|
||||||
//set body as html
|
from: `"Tokebot🤖💨"<${config.mail.address}>`,
|
||||||
mailObj.html = body;
|
to,
|
||||||
//If we're sending plaintext
|
subject
|
||||||
}else{
|
};
|
||||||
//Set body as plaintext
|
|
||||||
mailObj.text = body
|
//If we're sending HTML
|
||||||
|
if(htmlBody){
|
||||||
|
//set body as html
|
||||||
|
mailObj.html = body;
|
||||||
|
//If we're sending plaintext
|
||||||
|
}else{
|
||||||
|
//Set body as plaintext
|
||||||
|
mailObj.text = body
|
||||||
|
}
|
||||||
|
|
||||||
|
//Send mail based on mail object
|
||||||
|
const sentMail = await transporter.sendMail(mailObj);
|
||||||
|
|
||||||
|
//return the mail info
|
||||||
|
return sentMail;
|
||||||
|
}catch(err){
|
||||||
|
loggerUtils.dumpError(err, new Date(), 'mail/');
|
||||||
}
|
}
|
||||||
|
|
||||||
//Send mail based on mail object
|
|
||||||
const sentMail = await transporter.sendMail(mailObj);
|
|
||||||
|
|
||||||
//return the mail info
|
|
||||||
return sentMail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
46
src/views/migrate.ejs
Normal file
46
src/views/migrate.ejs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
<%# Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>. %>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<%- include('partial/styles', {instance, user}); %>
|
||||||
|
<%- include('partial/csrfToken', {csrfToken}); %>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/migrate.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css">
|
||||||
|
<title><%= instance %> - Account Migration</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%- include('partial/navbar', {user}); %>
|
||||||
|
<form action="javascript:">
|
||||||
|
<label>Username:</label>
|
||||||
|
<input class="migrate-prompt" id="migrate-username" placeholder="Required">
|
||||||
|
<label>Old Password:</label>
|
||||||
|
<input class="migrate-prompt" id="migrate-password-old" placeholder="Required" type="password">
|
||||||
|
<label>Password:</label>
|
||||||
|
<input class="migrate-prompt" id="migrate-password" placeholder="Required" type="password">
|
||||||
|
<label>Confirm Password:</label>
|
||||||
|
<input class="migrate-prompt" id="migrate-password-confirm" placeholder="Required" type="password">
|
||||||
|
<altcha-widget floating challengejson="<%= JSON.stringify(challenge) %>"></altcha-widget>
|
||||||
|
<button id="migrate-button" class='positive-button'>migrate</button>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
<footer>
|
||||||
|
<%- include('partial/scripts', {user}); %>
|
||||||
|
<script src="/js/migrate.js"></script>
|
||||||
|
<script src="/lib/altcha/altcha.js" type="module"></script>
|
||||||
|
</footer>
|
||||||
|
</html>
|
||||||
31
www/css/migrate.css
Normal file
31
www/css/migrate.css
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
form{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
margin: 5% 17%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.migrate-prompt{
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
#migrate-button{
|
||||||
|
width: 6em;
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
68
www/js/migrate.js
Normal file
68
www/js/migrate.js
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
|
class migratePrompt{
|
||||||
|
constructor(){
|
||||||
|
//Grab user prompt
|
||||||
|
this.user = document.querySelector("#migrate-username");
|
||||||
|
//Grab pass prompts
|
||||||
|
this.oldPass = document.querySelector("#migrate-password-old");
|
||||||
|
this.pass = document.querySelector("#migrate-password");
|
||||||
|
this.passConfirm = document.querySelector("#migrate-password-confirm");
|
||||||
|
//Grab migrate button
|
||||||
|
this.button = document.querySelector("#migrate-button");
|
||||||
|
//Grab altcha widget
|
||||||
|
this.altcha = document.querySelector("altcha-widget");
|
||||||
|
//Setup null property to hold verification payload from altcha widget
|
||||||
|
this.verification = null
|
||||||
|
|
||||||
|
//Run input setup after DOM content has completely loaded to ensure altcha event listeners work
|
||||||
|
document.addEventListener('DOMContentLoaded', this.setupInput.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
setupInput(){
|
||||||
|
//Add verification event listener to altcha widget
|
||||||
|
this.altcha.addEventListener("verified", this.verify.bind(this));
|
||||||
|
|
||||||
|
//Add migrate event listener to migrate button
|
||||||
|
this.button.addEventListener("click", this.migrate.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(event){
|
||||||
|
//pull verification payload from event
|
||||||
|
this.verification = event.detail.payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate(){
|
||||||
|
//If altcha verification isn't complete
|
||||||
|
if(this.verification == null){
|
||||||
|
//don't bother
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the confirmation password doesn't match
|
||||||
|
if(this.pass.value != this.passConfirm.value){
|
||||||
|
//Scream and shout
|
||||||
|
new canopyUXUtils.popup(`<p>Confirmation password does not match!</p>`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Send the registration informaiton off to the server
|
||||||
|
utils.ajax.migrate(this.user.value , this.oldPass.value, this.pass.value , this.passConfirm.value , this.verification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const migrateForm = new migratePrompt();
|
||||||
Loading…
Reference in a new issue