Add option to turn off sending mails

Remove GMAIL support (they shut down 3rd party apps anyway) and add note
to ssmtp, which can be used to still send over other providers.
This commit is contained in:
HF 2022-06-30 11:47:36 +02:00
parent 2575e54ba0
commit 491cbb1abe
5 changed files with 105 additions and 113 deletions

View File

@ -9,7 +9,7 @@ Official repository of [pixelplanet.fun](http://www.pixelplanet.fun).
> **TRANSLATORS NEEDED** If you want to help us translate pixelplanet.fun, look into [i18n](./i18n)
Just to the 2nd anniversary of r/space, pixelplanet takes pixelgames to a new level. Place pixels, create pixelart and fight faction wars on pixelplanet.fun.
To the 2nd anniversary of r/space, pixelplanet takes pixelgames to a new level. Place pixels, create pixelart and fight faction wars on pixelplanet.fun.
Pixelplanet is a 65k x 65k large canvas that is a map of the world and can also be seen as 3d globe, you can place pixels where ever you want, build an island, take over another country with a flag or just create pixelart.
30 well chosen colors (decided by polls within the community) are available and you can place a pixel every 3s on an empty space, and 5s on an already set pixel. But pixels can be stacked up to a minute, so you don't have to wait every time.
@ -63,7 +63,7 @@ git config --global url.https://github.com/.insteadOf git://github.com/
- mysql or mariadb ([setup own user](https://www.digitalocean.com/community/tutorials/how-to-create-a-new-user-and-grant-permissions-in-mysql) and [create database](https://www.w3schools.com/SQl/sql_create_db.asp) for pixelplanet) for storing additional data like IP blacklist
### Configuration
Configuration takes place in the environment variables that are defined in ecosystem.yml
Configuration takes place in the environment variables that are defined in ecosystem.yml.
#### Neccessary Configuration
@ -79,32 +79,24 @@ Configuration takes place in the environment variables that are defined in ecosy
#### Optional Configuration
| Variable | Description | Example |
|-------------------|:--------------------------------------|-------------------------|
| ASSET_SERVER | URL for assets | "http://localhost" |
| USE_PROXYCHECK | Check users for Proxies | 0 |
| APISOCKET_KEY | Key for API Socket for SpecialAccess™ | "SDfasife3" |
| ADMIN_IDS | Ids of users with Admin rights | "1,12,3" |
| CAPTCHA_TIME | time in minutes between captchas | 30 |
| | 0: always captcha -1: never captcha | |
| SESSION_SECRET | random sting for express sessions | "ayylmao" |
| LOG_MYSQL | if sql queries should get logged | 0 |
| USE_XREALIP | see ngins / CDN section | 1 |
| BACKUP_URL | url of backup server (see Backup) | "http://localhost" |
| BACKUP_DIR | mounted directory of backup server | "/mnt/backup/" |
| GMAIL_USER | gmail username if used for mails | "ppfun@gmail.com" |
| GMAIL_PW | gmail password if used for mails | "lolrofls" |
| HOURLY_EVENT | run hourly void event on main canvas | 1 |
| Variable | Description | Example |
|-------------------|:--------------------------------------|---------------------------|
| ASSET_SERVER | URL for assets | "http://localhost" |
| USE_PROXYCHECK | Check users for Proxies | 0 |
| APISOCKET_KEY | Key for API Socket for SpecialAccess™ | "SDfasife3" |
| ADMIN_IDS | Ids of users with Admin rights | "1,12,3" |
| CAPTCHA_TIME | time in minutes between captchas | 30 |
| | 0: always captcha -1: never captcha | |
| SESSION_SECRET | random sting for express sessions | "ayylmao" |
| LOG_MYSQL | if sql queries should get logged | 0 |
| USE_XREALIP | see ngins / CDN section | 1 |
| BACKUP_URL | url of backup server (see Backup) | "http://localhost" |
| BACKUP_DIR | mounted directory of backup server | "/mnt/backup/" |
| HOURLY_EVENT | run hourly void event on main canvas | 1 |
| USE_MAILER | enable to use mail sevicse | 0 |
| MAIL_ADDRESS | email address for sending mails | "noreply@pixelplanet.fun" |
Notes:
- HOST / PORT is the host on which the ppfun server is listening. In example: If you have a reverse proxy on the same machine, HOST should still be unset or localhost, because it's where the proxy forwards to.
- to be able to use USE_PROXYCHECK, you have to have an account on proxycheck.io or getipintel or another checker setup and you might set some proxies in`proxies.json` that get used for making proxycheck requests. Look into `src/isProxy.js` to see how things work, but keep in mind that this isn't neccessarily how pixelplanet.fun uses it.
- Admins are users with 0cd and access to `Admintools`in their User Menu for image-upload and whatever
- You can find out the id of a user by looking into the logs (i.e. `info: {ip} / {id} wants to place 2 in (1701, -8315)`) when he places a pixel or by checking the MySql Users database
- If you use gmail as mail transport, make sure that less-secure apps are allowed to access it in your settings [here](https://myaccount.google.com/lesssecureapps)
#### Social Media
#### Social Media Configuration
| Variable | Description |
|-----------------------|:-------------------------|
@ -122,33 +114,45 @@ Notes:
Notes:
- The HTML for SocialMedia logins is in src/componets/UserAreaModal.js , delete stuff from there if you don't need it
- The HTML for the Help Screen is in src/components/HelpModal.js
- HOST / PORT is the host on which the ppfun server is listening. In example: If you have a reverse proxy on the same machine, HOST should still be unset or localhost, because it's where the proxy forwards to.
- to be able to use USE_PROXYCHECK, you have to have an account on proxycheck.io or getipintel or another checker setup and you might set some proxies in`proxies.json` that get used for making proxycheck requests. Look into `src/isProxy.js` to see how things work, but keep in mind that this isn't neccessarily how pixelplanet.fun uses it.
- Admins are users with 0cd and access to `Admintools`in their User Menu for image-upload and whatever
- You can find out the id of a user by looking into the logs (i.e. `info: {ip} / {id} wants to place 2 in (1701, -8315)`) when he places a pixel or by checking the MySql Users database
- pixelplanet uses the unix command sendmail for sending verification and password reset mails. If you don't want to set up your own mail server, look into [ssmtp](https://wiki.archlinux.org/title/SSMTP), which provides a sendmail interface that forwards to other providers like gmail.
- default configuartion values can be seen in `src/core/config.js`
Notes:
- The HTML for SocialMedia logins is in src/componets/UserAreaModal.js , delete stuff from there if you don't need it. The HTML for the Help Screen is in src/components/HelpModal.js
Canvas specific configuartion like colors and cooldown is in `canvases.json` for all canvases. The titles and descriptions of the canvases are in `src/canvasesDesc.js` for translation reasons. Changing them requires a rebuild.
Meaning of some values:
#### Neccessary canvases Configuration
| Key | Description |
|--------|:----------------------------------------------------------------|
| ident | Unique character used in the url |
| size | canvas size, power of 2 and between 256 and 65536 |
| bcd | Base cooldown for unset pixels |
| pcd | Cooldown for placing on set pixels (defaults to same as bcd) |
| cds | Stack time of Cooldown |
| sd | Start-date of the canvas (for historical view) |
#### Optional canvases Configuration
| Key | Description |
|--------|:----------------------------------------------------------------|
| pcd | Cooldown for placing on set pixels (defaults to same as bcd) |
| cli | Number of leading colors on the palette to ignore (default: 0) |
| req | requieremt to place on the canvas (default: unset) |
| ranked | If pixels on canvas count on player statistic (default: false) |
| req | requieremt to place on the canvas |
| sd | Start-date of the canvas for historical view |
| v | If 3D voxel canvas (boolean) (default: false) |
| hid | Hidden canvases, can be just seen by pressing P (default: false)|
Values that have defaults and `req` are optional.
The canvas size limit can be surpassed by changing the websocket packages in src/socket/packages/ to send chunk coordinates in 16bit.
If `req` is 0, the canvas is only available for registered Useers. If it is a number >0 it is the amount of total pixels placed before a player is allowed to play there. If it is `top`, then it is only accessible for the Top10 players of the previous day.
The colors that are ignored via `cli` are used for making the canvas (blue ocean and white continents) and to know if the pixel is already set by a user or not.
If you want to add a new canvas, be sure that you additionally create `public/loading${canvasId}.png`, `public/assets3d/normal${canvasId}.jpg`, `public/preview${canvasId}.png` and `public/assets3d/specular${canvasId}.jpg`, check out the existing ones to see what those files are for.
Notes:
The default configuration values can be seen in `src/core/config.js` and for the canvases in `src/core/constats.js`
- The canvas size limit can be surpassed by changing the websocket packages in src/socket/packages/ to send chunk coordinates in 16bit.
- If `req` is 0, the canvas is only available for registered Useers. If it is a number >0 it is the amount of total pixels placed before a player is allowed to play there. If it is `top`, then it is only accessible for the Top10 players of the previous day.
- The colors that are ignored via `cli` are used for making the canvas (blue ocean and white continents) and to know if the pixel is already set by a user or not.
- If you want to add a new canvas, be sure that you additionally create `public/loading${canvasId}.png`, `public/assets3d/normal${canvasId}.jpg`, `public/preview${canvasId}.png` and `public/assets3d/specular${canvasId}.jpg`, check out the existing ones to see what those files are for.
#### Styles
@ -163,10 +167,6 @@ To add more css styles, create a new css file in `src/styles` based on `src/styl
pm2 start ecosystem.yml
```
Notes:
- pixelplanet uses the unix command sendmail for sending verification and password reset mails if no GMAIL account is given. If you are on windows, this might not work.
### Logging
General logs are in `~/pm2/log/`, you can view them with
@ -187,9 +187,9 @@ Pixel placing logs are in `./log/pixels.log`and proxycheck logs in `./log/proxie
pm2 stop ppfun-server
```
### If using Cloudflare / Reverse Proxy
### If using reverse Proxy
If USE\_XREALIP is set, we take the IP from the X-Real-Ip header. Use this if you have pixelplanet running behind nginx and cloudflare. Use the nginx set\_realip module to give us the client ip on the X-Real-Ip header (and set it up so that just cloudflare are trusted proxy IPs, or else players could fake their IP). And be sure to also set X-Forwarded-Host, X-Forwarded-Port and set X-Forwarded-Proto, because we use it for CORS and redirecion.
If USE\_XREALIP is set, we take the IP from the X-Real-Ip header. Use this if you have pixelplanet running behind a reverse proxy like nginx (recommended). Use the nginx set\_realip module to give us the client ip on the X-Real-Ip header (and set it up so that just cloudflare are trusted proxy IPs, if you use them, or else players could fake their IP). And be sure to also set X-Forwarded-Host, X-Forwarded-Port and set X-Forwarded-Proto, because we use it for CORS and redirecion.
### Auto-Start
To have the canvas with all it's components autostart at systemstart,
@ -225,13 +225,14 @@ If command is defined, it will be executed after every backup (just one command,
You can run it with pm2, just like pixelplanet. An example ecosystem-backup.example.yml file will be located in the dist directory.
Note:
- You do not have to run backups or historical view, it's optional.
### Hourly Event
## Hourly Event
Hourly event is an MMORPG style event that launches once in two hours where users have to fight against a growing void that starts at a random position at the main canvas. If they complete it successfully, the whole canvas will have half cooldown for a few minutes.
### Historical view
## Historical view
![historicalview](promotion/historicalview.gif)
@ -249,7 +250,6 @@ If v is set and true for a canvas in the canvas.json, it will be a 3D voxel canv
## Development
Run `npm run lint:src` to check for code errors and warnings or `npm run lint -- ./your/file.js` to check a single file.
Please do not produce too many additional warnings.
[ttag](https://github.com/ttag-org/ttag/) is used for handling translations. For server-side rendering the `Accept-Language` header gets checked and the first locale used and on-the-fly translated (`src/core/ttag.js` provides the functions for it). On the client-side a seperate bundle for every language gets provided.
The language definitions in `i18n/template.pot` and `i18n/template-ssr.pot` get updated when doing a dev build with
@ -258,4 +258,4 @@ npm run build:dev
```
which also only builds the default local in a development environment for debugging.
You can use `npm run babel-node ./your/script.js` to execute a script with local babel.
You can use `npm run babel-node ./utils/script.js` to execute a script with local babel (path always relative to the root directory).

View File

@ -15,6 +15,7 @@ import { cheapDetector } from './isProxy';
import { DailyCron } from '../utils/cron';
import ttags from './ttag';
import { USE_MAILER } from './config';
import {
CHAT_CHANNELS,
EVENT_USER_NAME,
@ -415,7 +416,7 @@ export class ChatProvider {
displayCountry = 'bt';
}
if (!user.regUser.verified) {
if (USE_MAILER && !user.regUser.verified) {
return t`Your mail has to be verified in order to chat`;
}

View File

@ -12,8 +12,9 @@ if (process.env.BROWSER) {
export const PORT = process.env.PORT || 8080;
export const HOST = process.env.HOST || 'localhost';
export const GMAIL_USER = process.env.GMAIL_USER || null;
export const GMAIL_PW = process.env.GMAIL_PW || null;
export const USE_MAILER = parseInt(process.env.USE_MAILER, 10) || false;
export const MAIL_ADDRESS = process.env.MAIL_ADDRESS
|| 'donotreply@pixelplanet.fun';
const TILE_FOLDER_REL = process.env.TILE_FOLDER || 'tiles';
export const TILE_FOLDER = path.join(__dirname, `./${TILE_FOLDER_REL}`);
@ -51,13 +52,6 @@ export const APISOCKET_KEY = process.env.APISOCKET_KEY || null;
export const ADMIN_IDS = (process.env.ADMIN_IDS)
? process.env.ADMIN_IDS.split(',').map((z) => parseInt(z, 10)) : [];
export const analytics = {
// https://analytics.google.com/
google: {
trackingId: process.env.GOOGLE_TRACKING_ID, // UA-XXXXX-X
},
};
export const auth = {
// https://developers.facebook.com/
facebook: {

View File

@ -10,44 +10,52 @@ import logger from './logger';
import { HOUR, MINUTE } from './constants';
import { DailyCron, HourlyCron } from '../utils/cron';
import { getTTag } from './ttag';
import { GMAIL_USER, GMAIL_PW } from './config';
import { USE_MAILER, MAIL_ADDRESS } from './config';
import { RegUser } from '../data/sql';
/*
* define mail transport
* using unix command sendmail
*/
const transporter = (GMAIL_USER && GMAIL_PW)
? nodemailer.createTransport({
service: 'gmail',
auth: {
user: GMAIL_USER,
pass: GMAIL_PW,
},
})
: nodemailer.createTransport({
sendmail: true,
newline: 'unix',
path: '/usr/sbin/sendmail',
});
const address = (GMAIL_USER && GMAIL_PW)
? GMAIL_USER
: 'donotreply@pixelplanet.fun';
// TODO make code expire
class MailProvider {
constructor() {
this.clearCodes = this.clearCodes.bind(this);
this.enabled = !!USE_MAILER;
if (this.enabled) {
this.transporter = nodemailer.createTransport({
sendmail: true,
newline: 'unix',
path: '/usr/sbin/sendmail',
});
this.verifyCodes = {};
HourlyCron.hook(this.clearCodes);
DailyCron.hook(MailProvider.cleanUsers);
this.clearCodes = this.clearCodes.bind(this);
this.verifyCodes = {};
HourlyCron.hook(this.clearCodes);
DailyCron.hook(MailProvider.cleanUsers);
}
}
sendMail(to, subject, html) {
if (!this.enabled) {
return;
}
this.transporter.sendMail({
from: `PixelPlanet <${MAIL_ADDRESS}>`,
to,
replyTo: MAIL_ADDRESS,
subject,
html,
}, (err) => {
if (err) {
logger.error(err);
}
});
}
sendVerifyMail(to, name, host, lang) {
if (!this.enabled) {
return null;
}
const { t } = getTTag(lang);
const pastMail = this.verifyCodes[to];
@ -62,30 +70,27 @@ class MailProvider {
return t`We already sent you a verification mail, you can request another one in ${minLeft} minutes.`;
}
}
logger.info(`Sending verification mail to ${to} / ${name}`);
const code = this.setCode(to);
const verifyUrl = `${host}/api/auth/verify?token=${code}`;
transporter.sendMail({
from: `PixelPlanet <${address}>`,
to,
replyTo: address,
subject: t`Welcome ${name} to PixelPlanet, plese verify your mail`,
// text: `Hello,\nwelcome to our little community of pixelplacers, to use your account, you have to verify your mail. You can do that here:\n ${verifyUrl} \nHave fun and don't hesitate to contact us if you encouter any problems :)\nThanks`,
html: `<em>${t`Hello ${name}`}</em>,<br />
const subject = t`Welcome ${name} to PixelPlanet, plese verify your mail`;
const html = `<em>${t`Hello ${name}`}</em>,<br />
${t`welcome to our little community of pixelplacers, to use your account, you have to verify your mail. You can do that here: `} <a href="${verifyUrl}">${t`Click to Verify`}</a>. ${t`Or by copying following url:`}<br />${verifyUrl}\n<br />
${t`Have fun and don't hesitate to contact us if you encouter any problems :)`}<br />
${t`Thanks`}<br /><br />
<img alt="" src="https://assets.pixelplanet.fun/tile.png" style="height:64px; width:64px" />`,
}, (err) => {
if (err) {
logger.error(err);
}
});
<img alt="" src="https://assets.pixelplanet.fun/tile.png" style="height:64px; width:64px" />`;
this.sendMail(to, subject, html);
return null;
}
async sendPasswdResetMail(to, ip, host, lang) {
const { t } = getTTag(lang);
if (!this.enabled) {
return t`Mail is not configured on the server`;
}
const pastMail = this.verifyCodes[to];
if (pastMail) {
if (Date.now() < pastMail.timestamp + 15 * MINUTE) {
@ -102,6 +107,7 @@ class MailProvider {
);
return t`Couldn't find this mail in our database`;
}
/*
* not sure if this is needed yet
* does it matter if spaming password reset mails or verifications mails?
@ -115,21 +121,12 @@ class MailProvider {
logger.info(`Sending Password reset mail to ${to}`);
const code = this.setCode(to);
const restoreUrl = `${host}/reset_password?token=${code}`;
transporter.sendMail({
from: `PixelPlanet <${address}>`,
to,
replyTo: address,
subject: t`You forgot your password for PixelPlanet? Get a new one here`,
// text: `Hello,\nYou requested to get a new password. You can change your password within the next 30min here:\n ${restoreUrl} \nHave fun and don't hesitate to contact us if you encouter any problems :)\nIf you did not request this mail, please just ignore it (the ip that requested this mail was ${ip}).\nThanks`,
html: `<em>${t`Hello`}</em>,<br />
const subject = t`You forgot your password for PixelPlanet? Get a new one here`;
const html = `<em>${t`Hello`}</em>,<br />
${t`You requested to get a new password. You can change your password within the next 30min here: `} <a href="${restoreUrl}">${t`Reset Password`}</a>. ${t`Or by copying following url:`}<br />${restoreUrl}\n<br />
${t`If you did not request this mail, please just ignore it (the ip that requested this mail was ${ip}).`}<br />
${t`Thanks`}<br /><br />\n<img alt="" src="https://assets.pixelplanet.fun/tile.png" style="height:64px; width:64px" />`,
}, (err) => {
if (err) {
logger.error(err & err.stack);
}
});
${t`Thanks`}<br /><br />\n<img alt="" src="https://assets.pixelplanet.fun/tile.png" style="height:64px; width:64px" />`;
this.sendMail(to, subject, html);
return null;
}
@ -212,7 +209,6 @@ class MailProvider {
[Sequelize.Op.lt]:
Sequelize.literal('CURRENT_TIMESTAMP - INTERVAL 4 DAY'),
},
// NOTE: this means that minecraft verified accounts do not get deleted
verified: 0,
},
});

View File

@ -7,6 +7,7 @@
*/
// eslint-disable-next-line import/no-unresolved
import { getLocalicedCanvases } from '../canvasesDesc';
import { USE_MAILER } from './config';
import chatProvider from './ChatProvider';
@ -18,7 +19,7 @@ export default async function getMe(user, lang = 'default') {
} = userdata;
if (!name) userdata.name = null;
const messages = [];
if (name && !mailVerified) {
if (USE_MAILER && name && !mailVerified) {
messages.push('not_verified');
}
if (messages.length > 0) {