This commit is contained in:
Troplo 2022-07-31 14:56:43 +10:00
parent fcd847cf53
commit b93948fc36
21 changed files with 594 additions and 31 deletions

View file

@ -23,6 +23,10 @@ Colubrina is a simple self-hostable chatting platform written in Vue, and Vuetif
- [x] Embeds & MediaProxy
- [ ] Clean-up/refactor code
- [x] Mobile responsiveness/compatibility
- [x] Email verification
- [ ] Password resetting
- [ ] Channel message pins
- [ ] Read receipts
<img src="https://i.troplo.com/i/d608273e066c.png" alt="Chat" width="45%"></img>
<img src="https://i.troplo.com/i/e8e2c9d6e349.png" alt="Friends" width="45%"></img>

View file

@ -6,3 +6,10 @@ NOTIFICATION_TYPE=info
SITE_NAME=Colubrina
ALLOW_REGISTRATIONS=true
PUBLIC_USERS=false
EMAIL_VERIFICATION=false
EMAIL_SMTP_HOST=smtp.myhost.com
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USER=colubrina@myhost.com
EMAIL_SMTP_FROM=colubrina@myhost.com
EMAIL_SMTP_PASSWORD=myPassword
EMAIL_SMTP_SECURE=false

View file

@ -31,7 +31,8 @@ app.get("/api/v1/state", async (req, res) => {
latestVersion: require("../frontend/package.json").version,
name: process.env.SITE_NAME,
allowRegistrations: process.env.ALLOW_REGISTRATIONS === "true",
publicUsers: process.env.PUBLIC_USERS === "true"
publicUsers: process.env.PUBLIC_USERS === "true",
emailVerification: process.env.EMAIL_VERIFICATION === "true"
})
})

View file

@ -9,7 +9,7 @@ module.exports = async function (req, res, next) {
const user = await User.findOne({
where: { id: session.userId },
attributes: {
exclude: ["totp", "password"]
exclude: ["totp", "password", "emailToken"]
},
include: [
{

View file

@ -19,7 +19,7 @@ module.exports = async function (socket, next) {
const user = await User.findOne({
where: { id: session.userId },
attributes: {
exclude: ["totp", "password"]
exclude: ["totp", "password", "emailToken"]
},
include: [
{

View file

@ -39,7 +39,13 @@ let Errors = {
400
],
banned: ["You are banned from this instance.", 400],
leavingDirectChat: ["You cannot leave a direct message.", 400]
leavingDirectChat: ["You cannot leave a direct message.", 400],
emailVerificationRequired: [
"You must verify your email before you can do this action.",
401
],
mailFail: ["There was an error sending the verification email.", 400],
invalidToken: ["The token you provided is invalid.", 400]
}
function processErrors(errorName) {

View file

@ -0,0 +1,23 @@
"use strict"
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn("Users", "emailVerified", {
type: Sequelize.BOOLEAN,
defaultValue: false
})
await queryInterface.addColumn("Users", "emailToken", {
type: Sequelize.STRING,
defaultValue: null
})
},
async down(queryInterface, Sequelize) {
/**
* Add reverting commands here.
*
* Example:
* await queryInterface.dropTable('users');
*/
}
}

View file

@ -41,8 +41,8 @@ module.exports = (sequelize, DataTypes) => {
msg: "Username is required"
},
len: {
args: [2, 16],
msg: "Username must be between 2 and 16 characters"
args: [2, 24],
msg: "Username must be between 2 and 24 characters"
},
regex: {
args: /^[a-z0-9_-]+$/i,
@ -158,6 +158,15 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
emailVerified: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
emailToken: {
type: DataTypes.STRING,
allowNull: true
}
},
{

View file

@ -25,9 +25,11 @@
"jade": "~1.11.0",
"jw-paginate": "^1.0.4",
"local-cors-proxy": "^1.1.0",
"mailgen": "^2.0.27",
"mariadb": "^3.0.1",
"multer": "^1.4.4",
"node-xwhois": "^2.0.10",
"nodemailer": "^6.7.7",
"open-graph-scraper": "^4.11.0",
"patch-package": "^6.4.7",
"pg": "^8.7.3",

View file

@ -135,6 +135,18 @@ async function createMessage(req, type, content, association, userId) {
})
}
router.all("*", auth, async (req, res, next) => {
try {
if (!req.user.emailVerified && process.env.EMAIL_VERIFICATION === "true") {
throw Errors.emailVerificationRequired
} else {
next()
}
} catch (e) {
next(e)
}
})
router.get("/", auth, async (req, res, next) => {
try {
let chats = await ChatAssociation.findAll({

View file

@ -15,6 +15,8 @@ const semver = require("semver")
const multer = require("multer")
const FileType = require("file-type")
const rateLimit = require("express-rate-limit")
const Mailgen = require("mailgen")
const nodemailer = require("nodemailer")
const storage = multer.diskStorage({
destination: function (req, file, cb) {
@ -38,6 +40,16 @@ const limiter = rateLimit({
keyGenerator: (req, res) => req.user?.id || req.ip
})
const mailLimiter = rateLimit({
windowMs: 60 * 1000,
max: 1,
message: Errors.rateLimit,
standardHeaders: true,
legacyHeaders: false,
skipFailedRequests: true,
keyGenerator: (req, res) => req.user?.id || req.ip
})
const whitelist = [
"image/png",
"image/jpeg",
@ -58,6 +70,92 @@ const upload = multer({
}
})
router.post("/verify/resend", auth, mailLimiter, async (req, res, next) => {
try {
if (process.env.EMAIL_VERIFICATION !== "true") {
throw Errors.invalidParameter("Email verification is disabled")
}
const token = "COLUBRINA-VERIFY-" + cryptoRandomString({ length: 64 })
await req.user.update({
emailToken: token
})
const mailGenerator = new Mailgen({
theme: "default",
product: {
name: process.env.SITE_NAME,
link: process.env.CORS_HOSTNAME
}
})
const email = {
body: {
name: req.user.username,
intro: `${process.env.SITE_NAME} Account Verification`,
action: {
instructions: `You are receiving this email because you registered on ${process.env.SITE_NAME}, please use the link below to verify your account.`,
button: {
color: "#1A97FF",
text: "Account Verification",
link: process.env.CORS_HOSTNAME + "/email/confirm/" + token
}
},
outro: "If you did not register, please disregard this email."
}
}
const emailBody = mailGenerator.generate(email)
const emailText = mailGenerator.generatePlaintext(email)
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_SMTP_HOST,
port: process.env.EMAIL_SMTP_PORT,
secure: JSON.parse(process.env.EMAIL_SMTP_SECURE.toLowerCase()),
auth: {
user: process.env.EMAIL_SMTP_USER,
pass: process.env.EMAIL_SMTP_PASSWORD
}
})
let info = await transporter.sendMail({
from: process.env.EMAIL_SMTP_FROM,
to: req.user.email,
subject: "Email Verification - " + process.env.SITE_NAME,
text: emailText,
html: emailBody
})
if (info) {
res.json({ success: true })
} else {
throw Errors.mailFail
}
} catch (e) {
next(e)
}
})
router.post("/verify/confirm/:token", auth, async (req, res, next) => {
try {
const user = await User.findOne({
where: {
id: req.user.id
}
})
if (process.env.EMAIL_VERIFICATION !== "true") {
throw Errors.invalidParameter("Email verification is disabled")
}
if (!req.params.token) {
throw Errors.invalidToken
}
if (req.params.token !== user.emailToken) {
throw Errors.invalidToken
}
await user.update({
emailVerified: true,
emailToken: null
})
res.json({ success: true })
} catch (e) {
next(e)
}
})
router.post("/login", async (req, res, next) => {
async function checkPassword(password, hash) {
try {

View file

@ -266,6 +266,11 @@ align-text@^0.1.1, align-text@^0.1.3:
longest "^1.0.1"
repeat-string "^1.5.2"
ansi-colors@^4.1.1:
version "4.1.3"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==
ansi-escapes@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
@ -293,7 +298,7 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
ansi-styles@^4.0.0:
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
@ -369,6 +374,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==
async@^3.2.3:
version "3.2.4"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -506,6 +516,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
brace-expansion@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
dependencies:
balanced-match "^1.0.0"
braces@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@ -652,6 +669,14 @@ chalk@^2.3.2, chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^4.0.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
character-parser@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-1.2.1.tgz#c0dde4ab182713b919b970959a123ecc1a30fcd6"
@ -674,7 +699,7 @@ cheerio-select@^2.1.0:
domhandler "^5.0.3"
domutils "^3.0.1"
cheerio@^1.0.0-rc.11:
cheerio@^1.0.0-rc.11, cheerio@^1.0.0-rc.3:
version "1.0.0-rc.12"
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683"
integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==
@ -827,6 +852,11 @@ commander@^2.19.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
commander@~2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d"
@ -1102,6 +1132,15 @@ dicer@0.2.5:
readable-stream "1.1.x"
streamsearch "0.1.2"
dom-serializer@^1.0.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.2.0"
entities "^2.0.0"
dom-serializer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
@ -1111,11 +1150,25 @@ dom-serializer@^2.0.0:
domhandler "^5.0.2"
entities "^4.2.0"
domelementtype@^2.3.0:
domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
domhandler@^3.0.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a"
integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==
dependencies:
domelementtype "^2.0.1"
domhandler@^4.2.0:
version "4.3.1"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
dependencies:
domelementtype "^2.2.0"
domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
@ -1123,6 +1176,15 @@ domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3:
dependencies:
domelementtype "^2.3.0"
domutils@^2.0.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.2.0"
domhandler "^4.2.0"
domutils@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c"
@ -1165,6 +1227,13 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
ejs@^3.1.6:
version "3.1.8"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b"
integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==
dependencies:
jake "^10.8.5"
emittery@^0.10.2:
version "0.10.2"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933"
@ -1215,6 +1284,11 @@ engine.io@~6.2.0:
engine.io-parser "~5.0.3"
ws "~8.2.3"
entities@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
entities@^4.2.0, entities@^4.3.0:
version "4.3.1"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4"
@ -1271,6 +1345,11 @@ escalade@^3.1.1:
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
escape-goat@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-3.0.0.tgz#e8b5fb658553fe8a3c4959c316c6ebb8c842b19c"
integrity sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@ -1395,6 +1474,13 @@ file-type@16.5.4:
strtok3 "^6.2.4"
token-types "^4.1.1"
filelist@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==
dependencies:
minimatch "^5.0.1"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@ -1640,6 +1726,11 @@ has-flag@^3.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
@ -1657,6 +1748,21 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
htmlparser2@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78"
integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==
dependencies:
domelementtype "^2.0.1"
domhandler "^3.0.0"
domutils "^2.0.0"
entities "^2.0.0"
htmlparser2@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010"
@ -1966,6 +2072,16 @@ jade@~1.11.0:
void-elements "~2.0.1"
with "~4.0.0"
jake@^10.8.5:
version "10.8.5"
resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46"
integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==
dependencies:
async "^3.2.3"
chalk "^4.0.2"
filelist "^1.0.1"
minimatch "^3.0.4"
js-beautify@^1.14.0:
version "1.14.4"
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.14.4.tgz#187d600a835f84de67a6d09ceaf3f199b7284c82"
@ -2055,6 +2171,17 @@ jszip@latest:
readable-stream "~2.3.6"
setimmediate "^1.0.5"
juice@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/juice/-/juice-8.0.0.tgz#ac77d3372373409b06a875aee425b9d381f645fe"
integrity sha512-LRCfXBOqI1wt+zYR/5xwDnf+ZyiJiDt44DGZaBSAVwZWyWv3BliaiGTLS6KCvadv3uw6XGiPPFcTfY7CdF7Z/Q==
dependencies:
cheerio "^1.0.0-rc.3"
commander "^6.1.0"
mensch "^0.3.4"
slick "^1.12.2"
web-resource-inliner "^5.0.0"
jw-paginate@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/jw-paginate/-/jw-paginate-1.0.4.tgz#c6e7dbb4a6e9d62e501c7f9562a3a2a33d77c9b9"
@ -2164,6 +2291,15 @@ lru-queue@^0.1.0:
dependencies:
es5-ext "~0.10.2"
mailgen@^2.0.27:
version "2.0.27"
resolved "https://registry.yarnpkg.com/mailgen/-/mailgen-2.0.27.tgz#aa4a6eb67bc97568288286025f535bef394f23aa"
integrity sha512-k+Q02hK/gI224JRw9FFW/yKgo3SbYOCTbfE0MsXx0YFOR9XMsdOPBhAbHH6fKvyDX6y1r4zvw9QIponQOippFQ==
dependencies:
ejs "^3.1.6"
he "^1.2.0"
juice "^8.0.0"
make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@ -2238,6 +2374,11 @@ memoizee@^0.4.15:
next-tick "^1.1.0"
timers-ext "^0.1.7"
mensch@^0.3.4:
version "0.3.4"
resolved "https://registry.yarnpkg.com/mensch/-/mensch-0.3.4.tgz#770f91b46cb16ea5b204ee735768c3f0c491fecd"
integrity sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@ -2273,6 +2414,11 @@ mime@1.6.0:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
mime@^2.4.6:
version "2.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
mimic-response@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
@ -2283,13 +2429,20 @@ mimic-response@^3.1.0:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
minimatch@^3.0.2, minimatch@^3.1.1:
minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
minimatch@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
dependencies:
brace-expansion "^2.0.1"
minimist@^1.2.0, minimist@^1.2.6:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
@ -2437,7 +2590,7 @@ node-addon-api@^5.0.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501"
integrity sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==
node-fetch@^2.6.7:
node-fetch@^2.6.0, node-fetch@^2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@ -2472,6 +2625,11 @@ node-xwhois@^2.0.10:
tar-stream latest
whois latest
nodemailer@^6.7.7:
version "6.7.7"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.7.tgz#e522fbd7507b81c51446d3f79c4603bf00083ddd"
integrity sha512-pOLC/s+2I1EXuSqO5Wa34i3kXZG3gugDssH+ZNCevHad65tc8vQlCQpOLaUjopvkRQKm2Cki2aME7fEOPRy3bA==
nopt@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
@ -3214,6 +3372,11 @@ slash@^2.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
slick@^1.12.2:
version "1.12.2"
resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7"
integrity sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==
smart-buffer@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
@ -3413,6 +3576,13 @@ supports-color@^5.3.0:
dependencies:
has-flag "^3.0.0"
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
@ -3673,6 +3843,11 @@ uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
valid-data-url@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/valid-data-url/-/valid-data-url-3.0.1.tgz#826c1744e71b5632e847dd15dbd45b9fb38aa34f"
integrity sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==
validator@^13.7.0:
version "13.7.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857"
@ -3697,6 +3872,18 @@ void-elements@~2.0.1:
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==
web-resource-inliner@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/web-resource-inliner/-/web-resource-inliner-5.0.0.tgz#ac30db8096931f20a7c1b3ade54ff444e2e20f7b"
integrity sha512-AIihwH+ZmdHfkJm7BjSXiEClVt4zUFqX4YlFAzjL13wLtDuUneSaFvDBTbdYRecs35SiU7iNKbMnN+++wVfb6A==
dependencies:
ansi-colors "^4.1.1"
escape-goat "^3.0.0"
htmlparser2 "^4.0.0"
mime "^2.4.6"
node-fetch "^2.6.0"
valid-data-url "^3.0.0"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"

View file

@ -159,11 +159,12 @@ async function createUser() {
email: await input.text("Email", {
default: "troplo@troplo.com"
}),
admin: JSON.parse(
await input.confirm("Admin (true/false)", {
admin: await input.confirm("Admin (true/false)", {
default: false
}),
emailVerified: await input.confirm("Email verified (true/false)", {
default: true
})
)
}
const { User } = require("../backend/models")
await User.create(user)
@ -231,6 +232,52 @@ async function configureDotEnv() {
default: false
})
)
const emailVerify = await input.text("Enforce email verification?", {
default: false
})
if (emailVerify) {
setEnvValue("EMAIL_VERIFICATION", true)
setEnvValue(
"EMAIL_SMTP_HOST",
await input.text("SMTP Host", {
default: "smtp.myhost.com"
})
)
setEnvValue(
"EMAIL_SMTP_PORT",
await input.text("SMTP Port", {
default: 587
})
)
setEnvValue(
"EMAIL_SMTP_USER",
await input.text("SMTP User", {
default: "colubrina@myhost.com"
})
)
setEnvValue(
"EMAIL_SMTP_FROM",
await input.text("SMTP From Address", {
default: "colubrina@myhost.com"
})
)
setEnvValue("EMAIL_SMTP_PASSWORD", await input.text("SMTP Password", {}))
setEnvValue(
"EMAIL_SMTP_SECURE",
await input.text("SMTP Secure", {
default: false
})
)
} else {
setEnvValue("EMAIL_VERIFICATION", false)
setEnvValue("EMAIL_SMTP_HOST", "smtp.myhost.com")
setEnvValue("EMAIL_SMTP_PORT", "587")
setEnvValue("EMAIL_SMTP_USER", "colubrina@myhost.com")
setEnvValue("EMAIL_SMTP_FROM", "colubrina@myhost.com")
setEnvValue("EMAIL_SMTP_PASSWORD", "myPassword")
setEnvValue("EMAIL_SMTP_SECURE", false)
}
setEnvValue("PUBLIC_USERS")
setEnvValue("NOTIFICATION", "")
setEnvValue("NOTIFICATION_TYPE", "info")
setEnvValue("RELEASE", "stable")

View file

@ -1,6 +1,6 @@
{
"name": "colubrina-chat",
"version": "1.0.7",
"version": "1.0.8",
"private": true,
"author": "Troplo <troplo@troplo.com>",
"license": "GPL-3.0",

View file

@ -765,7 +765,21 @@ export default {
this.$store.dispatch("getState")
this.getThemes()
this.communicationsIdleCheck()
this.$store.dispatch("getUserInfo").catch(() => {
this.$store
.dispatch("getUserInfo")
.then(() => {
console.log(window.location.pathname)
// check if its /email/confirm/<token>
if (
!window.location.pathname.includes("/email/confirm/") &&
!window.location.pathname.includes("/email/verify") &&
!this.$store.state.user.emailVerified &&
this.$store.state.site.emailVerification
) {
this.$router.push("/email/verify")
}
})
.catch(() => {
if (!["/login", "/register"].includes(this.$route.path)) {
this.$router.push("/login")
}

View file

@ -150,6 +150,22 @@ const routes = [
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue")
},
{
path: "/email/verify",
name: "Email Verify",
component: () =>
import(
/* webpackChunkName: "emailVerify" */ "../views/Email/EmailVerify.vue"
)
},
{
path: "/email/confirm/:token",
name: "Email Confirm",
component: () =>
import(
/* webpackChunkName: "emailConfirm" */ "../views/Email/EmailConfirm.vue"
)
},
{
path: "*",
name: "Not Found",

View file

@ -39,7 +39,8 @@ export default new Vuex.Store({
site: {
release: "stable",
loading: true,
name: "Colubrina"
name: "Colubrina",
emailVerification: false
},
user: {
bcUser: null,
@ -135,11 +136,14 @@ export default new Vuex.Store({
getChats(context) {
Vue.axios.defaults.headers.common["Authorization"] =
localStorage.getItem("session")
Vue.axios.get("/api/v1/communications").then((res) => {
Vue.axios
.get("/api/v1/communications")
.then((res) => {
context.commit("setChats", res.data)
context.dispatch("getCommunicationsUnread")
context.dispatch("updateQuickSwitch")
})
.catch(() => {})
},
getCommunicationsUnread(context) {
Vue.axios
@ -152,6 +156,7 @@ export default new Vuex.Store({
context.state.communicationNotifications += item.unread
})
})
.catch(() => {})
},
discardTheme(context) {
context.state.themeEngine.theme = {

View file

@ -0,0 +1,60 @@
<template>
<div>
<v-container>
<v-card color="card" class="rounded-xl">
<v-toolbar color="toolbar">
<v-toolbar-title> Email Confirmation </v-toolbar-title>
</v-toolbar>
<v-container v-if="loading" class="text-center justify-center">
<v-progress-circular
size="64"
:indeterminate="true"
class="mb-3"
></v-progress-circular>
<h3>We're currently confirming your email address. Please wait.</h3>
</v-container>
<v-container v-else-if="failed" class="text-center justify-center">
<v-icon size="72"> mdi-alert-outline </v-icon>
<h3>
We were unable to verify your email address, your code may be
incorrect, or have expired.
</h3>
</v-container>
<v-container v-else class="text-center justify-center">
<v-icon size="72"> mdi-check-circle </v-icon>
<h3>Your email has been verified!</h3>
</v-container>
</v-card>
</v-container>
</div>
</template>
<script>
import AjaxErrorHandler from "@/lib/errorHandler"
export default {
name: "EmailConfirm",
data() {
return {
failed: false,
loading: true
}
},
mounted() {
this.axios
.post("/api/v1/user/verify/confirm/" + this.$route.params.token)
.then(() => {
this.loading = false
this.$store.dispatch("getUserInfo")
this.$store.dispatch("getChats")
})
.catch((e) => {
this.loading = false
this.failed = true
AjaxErrorHandler(this.$store)(e)
})
}
}
</script>
<style scoped></style>

View file

@ -0,0 +1,54 @@
<template>
<div>
<v-container>
<v-card color="card" class="rounded-xl">
<v-toolbar color="toolbar">
<v-toolbar-title> Email Verification </v-toolbar-title>
</v-toolbar>
<v-container>
<h3>
Hi, we've sent an email to {{ $store.state.user.email }}, please
follow the instructions provided.
</h3>
<p class="mt-2">Haven't received it?</p>
<v-btn color="primary" text @click="resend" :loading="loading"
>Resend</v-btn
>
</v-container>
</v-card>
</v-container>
</div>
</template>
<script>
import AjaxErrorHandler from "@/lib/errorHandler"
export default {
name: "EmailVerify",
data() {
return {
loading: true
}
},
methods: {
resend() {
this.loading = true
this.axios
.post("/api/v1/user/verify/resend")
.then(() => {
this.loading = false
this.$toast.success("Email verification email sent successfully.")
})
.catch((e) => {
this.loading = false
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.resend()
}
}
</script>
<style scoped></style>

View file

@ -123,7 +123,14 @@ export default {
this.loading = false
this.$socket.disconnect()
this.$socket.connect()
if (
this.$store.state.site.emailVerification &&
!this.$store.state.user.emailVerified
) {
this.$router.push("/email/verify")
} else {
this.$router.push("/")
}
})
.catch((e) => {
if (

View file

@ -35,6 +35,10 @@
label="Password"
type="password"
></v-text-field>
<small v-if="$store.state.site.emailVerification"
>This instance has email verification enforced, ensure your
email is correct.</small
>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
@ -102,7 +106,14 @@ export default {
this.loading = false
this.$socket.disconnect()
this.$socket.connect()
if (
this.$store.state.site.emailVerification &&
!this.$store.state.user.emailVerified
) {
this.$router.push("/email/verify")
} else {
this.$router.push("/")
}
})
.catch((e) => {
if (