Skip to content

Commit e2ba154

Browse files
committed
adds setting to disable email/smtp
1 parent 41155da commit e2ba154

7 files changed

Lines changed: 144 additions & 69 deletions

File tree

api/src/controllers/user.js

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const register = (mail) => async (req, res, next) => {
110110
role,
111111
invitedBy: invite?.invitingUser,
112112
remainingInvites: 0,
113-
emailVerified: false,
113+
emailVerified: process.env.SQ_DISABLE_EMAIL,
114114
bonusPoints: 0,
115115
totp: {
116116
enabled: false,
@@ -125,19 +125,21 @@ export const register = (mail) => async (req, res, next) => {
125125

126126
const createdUser = await newUser.save();
127127

128-
const emailVerificationValidUntil = created + 48 * 60 * 60 * 1000;
129-
const emailVerificationToken = jwt.sign(
130-
{
131-
user: req.body.email,
132-
validUntil: emailVerificationValidUntil,
133-
},
134-
process.env.SQ_JWT_SECRET
135-
);
136-
await sendVerificationEmail(
137-
mail,
138-
req.body.email,
139-
emailVerificationToken
140-
);
128+
if (!process.env.SQ_DISABLE_EMAIL) {
129+
const emailVerificationValidUntil = created + 48 * 60 * 60 * 1000;
130+
const emailVerificationToken = jwt.sign(
131+
{
132+
user: req.body.email,
133+
validUntil: emailVerificationValidUntil,
134+
},
135+
process.env.SQ_JWT_SECRET
136+
);
137+
await sendVerificationEmail(
138+
mail,
139+
req.body.email,
140+
emailVerificationToken
141+
);
142+
}
141143

142144
if (createdUser) {
143145
if (req.body.invite) {
@@ -293,14 +295,16 @@ export const generateInvite = (mail) => async (req, res, next) => {
293295
const createdInvite = await invite.save();
294296

295297
if (createdInvite) {
296-
await mail.sendMail({
297-
from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`,
298-
to: email,
299-
subject: "Invite",
300-
text: `You have been invited to join ${process.env.SQ_SITE_NAME}. Please follow the link below to register.
298+
if (!process.env.SQ_DISABLE_EMAIL) {
299+
await mail.sendMail({
300+
from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`,
301+
to: email,
302+
subject: "Invite",
303+
text: `You have been invited to join ${process.env.SQ_SITE_NAME}. Please follow the link below to register.
301304
302305
${process.env.SQ_BASE_URL}/register?token=${createdInvite.token}`,
303-
});
306+
});
307+
}
304308
res.send(createdInvite);
305309
}
306310
} else {
@@ -343,18 +347,20 @@ export const changePassword = (mail) => async (req, res, next) => {
343347
{ $set: { password: hash } }
344348
);
345349

346-
await mail.sendMail({
347-
from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`,
348-
to: user.email,
349-
subject: "Your password was changed",
350-
text: `Your password was updated successfully at ${new Date().toISOString()} from ${
351-
req.ip
352-
}.
350+
if (!process.env.SQ_DISABLE_EMAIL) {
351+
await mail.sendMail({
352+
from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`,
353+
to: user.email,
354+
subject: "Your password was changed",
355+
text: `Your password was updated successfully at ${new Date().toISOString()} from ${
356+
req.ip
357+
}.
353358
354359
If you did not perform this action, follow the link below immediately to reset your password. If this was you, no action is required.
355360
356361
${process.env.SQ_BASE_URL}/reset-password/initiate`,
357-
});
362+
});
363+
}
358364

359365
res.sendStatus(200);
360366
} catch (e) {
@@ -388,14 +394,16 @@ export const initiatePasswordReset = (mail) => async (req, res, next) => {
388394
process.env.SQ_JWT_SECRET
389395
);
390396

391-
await mail.sendMail({
392-
from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`,
393-
to: user.email,
394-
subject: "Password reset",
395-
text: `Please follow the link below to reset your password.
397+
if (!process.env.SQ_DISABLE_EMAIL) {
398+
await mail.sendMail({
399+
from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`,
400+
to: user.email,
401+
subject: "Password reset",
402+
text: `Please follow the link below to reset your password.
396403
397404
${process.env.SQ_BASE_URL}/reset-password/finalise?token=${token}`,
398-
});
405+
});
406+
}
399407

400408
res.sendStatus(200);
401409
} catch (e) {

api/src/index.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,19 @@ validateConfig(config).then(() => {
5252
});
5353
}
5454

55-
const mail = nodemailer.createTransport({
56-
host: process.env.SQ_SMTP_HOST,
57-
port: process.env.SQ_SMTP_PORT,
58-
secure: process.env.SQ_SMTP_SECURE,
59-
auth: {
60-
user: process.env.SQ_SMTP_USER,
61-
pass: process.env.SQ_SMTP_PASS,
62-
},
63-
});
55+
let mail;
56+
57+
if (!process.env.SQ_DISABLE_EMAIL) {
58+
mail = nodemailer.createTransport({
59+
host: process.env.SQ_SMTP_HOST,
60+
port: process.env.SQ_SMTP_PORT,
61+
secure: process.env.SQ_SMTP_SECURE,
62+
auth: {
63+
user: process.env.SQ_SMTP_USER,
64+
pass: process.env.SQ_SMTP_PASS,
65+
},
66+
});
67+
}
6468

6569
const connectToDb = () => {
6670
console.log("[sq] initiating db connection...");

api/src/setup/createAdminUser.js

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const createAdminUser = async (mail) => {
1818
password: hash,
1919
created,
2020
remainingInvites: Number.MAX_SAFE_INTEGER,
21+
emailVerified: process.env.SQ_DISABLE_EMAIL,
2122
});
2223
adminUser.uid = crypto
2324
.createHash("sha256")
@@ -35,19 +36,21 @@ const createAdminUser = async (mail) => {
3536

3637
await adminUser.save();
3738

38-
const emailVerificationValidUntil = created + 48 * 60 * 60 * 1000;
39-
const emailVerificationToken = jwt.sign(
40-
{
41-
user: process.env.SQ_ADMIN_EMAIL,
42-
validUntil: emailVerificationValidUntil,
43-
},
44-
process.env.SQ_JWT_SECRET
45-
);
46-
await sendVerificationEmail(
47-
mail,
48-
process.env.SQ_ADMIN_EMAIL,
49-
emailVerificationToken
50-
);
39+
if (!process.env.SQ_DISABLE_EMAIL) {
40+
const emailVerificationValidUntil = created + 48 * 60 * 60 * 1000;
41+
const emailVerificationToken = jwt.sign(
42+
{
43+
user: process.env.SQ_ADMIN_EMAIL,
44+
validUntil: emailVerificationValidUntil,
45+
},
46+
process.env.SQ_JWT_SECRET
47+
);
48+
await sendVerificationEmail(
49+
mail,
50+
process.env.SQ_ADMIN_EMAIL,
51+
emailVerificationToken
52+
);
53+
}
5154

5255
console.log("[sq] created initial admin user");
5356
}

api/src/utils/validateConfig.js

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,31 @@ const configSchema = yup
6464
SQ_BASE_URL: yup.string().matches(httpRegex).required(),
6565
SQ_API_URL: yup.string().matches(httpRegex).required(),
6666
SQ_MONGO_URL: yup.string().matches(mongoRegex).required(),
67-
SQ_MAIL_FROM_ADDRESS: yup.string().email().required(),
68-
SQ_SMTP_HOST: yup.string().required(),
69-
SQ_SMTP_PORT: yup.number().integer().min(1).max(65535).required(),
70-
SQ_SMTP_SECURE: yup.boolean().required(),
67+
SQ_DISABLE_EMAIL: yup.boolean(),
68+
SQ_MAIL_FROM_ADDRESS: yup
69+
.string()
70+
.email()
71+
.when("SQ_DISABLE_EMAIL", {
72+
is: (val) => val !== true,
73+
then: (schema) => schema.required(),
74+
}),
75+
SQ_SMTP_HOST: yup.string().when("SQ_DISABLE_EMAIL", {
76+
is: (val) => val !== true,
77+
then: (schema) => schema.required(),
78+
}),
79+
SQ_SMTP_PORT: yup
80+
.number()
81+
.integer()
82+
.min(1)
83+
.max(65535)
84+
.when("SQ_DISABLE_EMAIL", {
85+
is: (val) => val !== true,
86+
then: (schema) => schema.required(),
87+
}),
88+
SQ_SMTP_SECURE: yup.boolean().when("SQ_DISABLE_EMAIL", {
89+
is: (val) => val !== true,
90+
then: (schema) => schema.required(),
91+
}),
7192
})
7293
.strict()
7394
.noUnknown()
@@ -77,8 +98,16 @@ const configSchema = yup
7798
SQ_JWT_SECRET: yup.string().required(),
7899
SQ_SERVER_SECRET: yup.string().required(),
79100
SQ_ADMIN_EMAIL: yup.string().email().required(),
80-
SQ_SMTP_USER: yup.string().required(),
81-
SQ_SMTP_PASS: yup.string().required(),
101+
SQ_SMTP_USER: yup.string(),
102+
SQ_SMTP_PASS: yup.string(),
103+
})
104+
.when("envs.SQ_DISABLE_EMAIL", {
105+
is: (val) => val !== true,
106+
then: (schema) => {
107+
schema.fields.SQ_SMTP_USER = yup.string().required();
108+
schema.fields.SQ_SMTP_PASS = yup.string().required();
109+
return schema;
110+
},
82111
})
83112
.strict()
84113
.noUnknown()

client/locales/en.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,24 @@
3131
"accEnable2FA": "Enable 2FA",
3232
"accEveryRequestYouFulfill": "for every request you fulfill, or",
3333
"accForEveryGBYouUpload": "for every GB you upload",
34-
"accIfYouAreAlsUploaderAcceptTorrent": "if you are als the uploader of the accepted torrent",
34+
"accIfYouAreAlsUploaderAcceptTorrent": "if you are also the uploader of the accepted torrent",
3535
"accInviteLinkCopiedClipboard": "Invite link copied to clipboard",
3636
"accInvites": "Invites",
3737
"accRemaining": "remaining",
3838
"accRoleUser": "Role: user",
3939
"accRoleAdmin": "Role: admin",
4040
"accInviteSentSuccess": "Invite sent successfully",
41+
"accInviteSentSuccessNoEmail": "Invite created successfully",
4142
"accInviteText1": "Enter an email address to send an invite. The invited user will need to sign up with the same email address. Once the invite is generated, you can also copy a direct invite link.",
43+
"accInviteText1NoEmail": "Enter an email address to generate an invite. The invited user will need to sign up with the same email address. This tracker has email sending disabled, so you will need to copy and send the invite link yourself.",
4244
"accItemsPurchasedSuccess": "Items purchased successfully",
4345
"accMyAccount": "My account",
4446
"accPassChangedSuccess": "Password changed successfully",
4547
"accPurchaseInvites": "Purchase invites",
4648
"accPurchaseUpload1GB": "Purchase upload (1 GB)",
4749
"accRole": "Role",
4850
"accSendInvite": "Send invite",
51+
"accSendInviteNoEmail": "Create invite",
4952
"accThisIsAdminAcc": "This is an admin account.",
5053
"accValidUntil": "Valid until",
5154
"accYouCurrentlyHave": "You currently have",
@@ -279,8 +282,8 @@
279282
"userUploaded": "Uploaded",
280283
"userMyUploads": "My uploads",
281284
"usernameRules": "Can only consist of letters, numbers, and “.”",
282-
"userUnban": "unban",
283-
"userBan": "ban",
285+
"userUnban": "Unban",
286+
"userBan": "Ban",
284287
"userUnbanned": "unbanned",
285288
"userBanned": "banned",
286289
"userAdmin": "Admin",

client/pages/account.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ const Account = ({ token, invites = [], user, userRole }) => {
9898
SQ_BP_COST_PER_INVITE,
9999
SQ_BP_COST_PER_GB,
100100
SQ_ALLOW_REGISTER,
101+
SQ_DISABLE_EMAIL,
101102
},
102103
} = getConfig();
103104

@@ -133,7 +134,14 @@ const Account = ({ token, invites = [], user, userRole }) => {
133134
return currentInvitesList;
134135
});
135136

136-
addNotification("success", `${getLocaleString("accInviteSentSuccess")}`);
137+
addNotification(
138+
"success",
139+
`${getLocaleString(
140+
SQ_DISABLE_EMAIL
141+
? "accInviteSentSuccessNoEmail"
142+
: "accInviteSentSuccess"
143+
)}`
144+
);
137145

138146
setRemainingInvites((r) => r - 1);
139147

@@ -425,7 +433,9 @@ const Account = ({ token, invites = [], user, userRole }) => {
425433
onClick={() => setShowInviteModal(true)}
426434
disabled={remainingInvites < 1}
427435
>
428-
{getLocaleString("accSendInvite")}
436+
{getLocaleString(
437+
SQ_DISABLE_EMAIL ? "accSendInviteNoEmail" : "accSendInvite"
438+
)}
429439
</Button>
430440
</Box>
431441
</Box>
@@ -650,7 +660,11 @@ const Account = ({ token, invites = [], user, userRole }) => {
650660
)}
651661
{showInviteModal && (
652662
<Modal close={() => setShowInviteModal(false)}>
653-
<Text mb={5}>{getLocaleString("accInviteText1")}</Text>
663+
<Text mb={5}>
664+
{getLocaleString(
665+
SQ_DISABLE_EMAIL ? "accInviteText1NoEmail" : "accInviteText1"
666+
)}
667+
</Text>
654668
<form onSubmit={handleGenerateInvite}>
655669
<Input
656670
name="email"
@@ -674,7 +688,11 @@ const Account = ({ token, invites = [], user, userRole }) => {
674688
>
675689
{getLocaleString("accCancel")}
676690
</Button>
677-
<Button>{getLocaleString("accSendInvite")}</Button>
691+
<Button>
692+
{getLocaleString(
693+
SQ_DISABLE_EMAIL ? "accSendInviteNoEmail" : "accSendInvite"
694+
)}
695+
</Button>
678696
</Box>
679697
</form>
680698
</Modal>

config.example.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,26 @@ module.exports = {
8585
// The URL of your MongoDB server. Under the recommended setup, it should be `mongodb://sq_mongodb/sqtracker`.
8686
SQ_MONGO_URL: "mongodb://sq_mongodb/sqtracker",
8787

88+
// Disables sending of any emails and removes the need for an SMTP server.
89+
// Fine for testing, not recommended in production as users will not be able to reset their passwords.
90+
SQ_DISABLE_EMAIL: false,
91+
8892
// The email address that mail will be sent from.
93+
// Not required if SQ_DISABLE_EMAIL=true.
8994
SQ_MAIL_FROM_ADDRESS: "mail@sqtracker.dev",
9095

9196
// The hostname of your SMTP server.
97+
// Not required if SQ_DISABLE_EMAIL=true.
9298
SQ_SMTP_HOST: "smtp.example.com",
9399

94100
// The port of your SMTP server.
101+
// Not required if SQ_DISABLE_EMAIL=true.
95102
SQ_SMTP_PORT: 587,
96103

97104
// Whether to force SMTP TLS: if true the connection will use TLS when connecting to server.
98105
// If false (the default) then TLS is used if server supports the STARTTLS extension.
99106
// In most cases set this value to true if you are connecting to port 465. For port 587 or 25 keep it false.
107+
// Not required if SQ_DISABLE_EMAIL=true.
100108
SQ_SMTP_SECURE: false,
101109
},
102110
secrets: {
@@ -111,9 +119,11 @@ module.exports = {
111119
SQ_ADMIN_EMAIL: "admin@example.com",
112120

113121
// The username to authenticate with your SMTP server with.
122+
// Not required if SQ_DISABLE_EMAIL=true.
114123
SQ_SMTP_USER: "smtp_username",
115124

116125
// The password to authenticate with your SMTP server with.
126+
// Not required if SQ_DISABLE_EMAIL=true.
117127
SQ_SMTP_PASS: "smtp_password",
118128
},
119129
};

0 commit comments

Comments
 (0)