diff --git a/README.md b/README.md index d09e98f..b33c7ba 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/src/core/ChatProvider.js b/src/core/ChatProvider.js index 63744aa..0f1d457 100644 --- a/src/core/ChatProvider.js +++ b/src/core/ChatProvider.js @@ -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`; } diff --git a/src/core/config.js b/src/core/config.js index 0f54dff..eb20cb5 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -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: { diff --git a/src/core/mail.js b/src/core/mail.js index 978227d..29eb7b2 100644 --- a/src/core/mail.js +++ b/src/core/mail.js @@ -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: `${t`Hello ${name}`},
+ const subject = t`Welcome ${name} to PixelPlanet, plese verify your mail`; + const html = `${t`Hello ${name}`},
${t`welcome to our little community of pixelplacers, to use your account, you have to verify your mail. You can do that here: `} ${t`Click to Verify`}. ${t`Or by copying following url:`}
${verifyUrl}\n
${t`Have fun and don't hesitate to contact us if you encouter any problems :)`}
${t`Thanks`}

- `, - }, (err) => { - if (err) { - logger.error(err); - } - }); + `; + 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: `${t`Hello`},
+ const subject = t`You forgot your password for PixelPlanet? Get a new one here`; + const html = `${t`Hello`},
${t`You requested to get a new password. You can change your password within the next 30min here: `} ${t`Reset Password`}. ${t`Or by copying following url:`}
${restoreUrl}\n
${t`If you did not request this mail, please just ignore it (the ip that requested this mail was ${ip}).`}
- ${t`Thanks`}

\n`, - }, (err) => { - if (err) { - logger.error(err & err.stack); - } - }); + ${t`Thanks`}

\n`; + 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, }, }); diff --git a/src/core/me.js b/src/core/me.js index e1fc6b2..37b0455 100644 --- a/src/core/me.js +++ b/src/core/me.js @@ -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) {