From 73e400f79a121ca103549a272de4d160da9e2bc2 Mon Sep 17 00:00:00 2001 From: HF Date: Thu, 4 Feb 2021 23:56:12 +0100 Subject: [PATCH 01/32] Add basic captchaserver --- src/captchaserver.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/captchaserver.js diff --git a/src/captchaserver.js b/src/captchaserver.js new file mode 100644 index 0000000..2266002 --- /dev/null +++ b/src/captchaserver.js @@ -0,0 +1,30 @@ +/* + * serving captchas + */ + +import process from 'process'; +import http from 'http'; +import ppfunCaptcha from 'ppfun-captcha'; + +/* +const [ + PORT, + REDIS_URL, +] = process.argv.slice(2); +*/ +const PORT = 7000; + +const server = http.createServer((req, res) => { + const captcha = ppfunCaptcha.create(); + console.log(`Serving ${captcha.text} to ${req.headers['x-real-ip']}`); + res.writeHead(200, { + 'Content-Type': 'text/html', + 'Cache-Control': 'no-cache', + }); + res.write(captcha.data); + res.end(); +}); + +server.listen(port, () => { + console.log(`Captcha Server listening on port ${port}`); +}); From 161bbb0eefe3ce5b1012a3072fc00dbf33fe7efb Mon Sep 17 00:00:00 2001 From: HF Date: Fri, 5 Feb 2021 00:17:25 +0100 Subject: [PATCH 02/32] remove cloudflare ip check and HOST option --- README.md | 9 ++--- deployment/example-ecosystem.yml | 4 +-- package-lock.json | 21 +++++++++++ package.json | 1 + src/captchaserver.js | 7 ++-- src/core/config.js | 2 -- src/utils/cloudflareip.js | 60 -------------------------------- src/utils/ip.js | 42 ++++++++-------------- src/web.js | 14 ++++---- 9 files changed, 54 insertions(+), 106 deletions(-) delete mode 100644 src/utils/cloudflareip.js diff --git a/README.md b/README.md index f861f5c..b2b7c88 100644 --- a/README.md +++ b/README.md @@ -69,8 +69,9 @@ Configuration takes place in the environment variables that are defined in ecosy | Variable | Description | Example | |----------------|:-------------------------|------------------------:| -| PORT | Port | 80 | -| REDIS_URL | URL:PORT of redis server | "redis://localhost:6379" | +| PORT | Own Port | 80 | +| HOST | Own Host | "localhost" | +| REDIS_URL | URL:PORT of redis server | "redis://localhost:6379"| | MYSQL_HOST | MySql Host | "localhost" | | MYSQL_USER | MySql User | "user" | | MYSQL_PW | MySql Password | "password" | @@ -90,7 +91,7 @@ Configuration takes place in the environment variables that are defined in ecosy | CAPTCHA_TIME | time in minutes between captchas | 30 | | SESSION_SECRET | random sting for express sessions | "ayylmao" | | LOG_MYSQL | if sql queries should get logged | 0 | -| USE_XREALIP | see cloudflare section | 1 | +| 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" | @@ -108,7 +109,7 @@ Notes: | Variable | Description | |-----------------------|:-------------------------| -| DISCORD_INVITE | Invite to discord server | +| GUILDED_INVITE | Invite to guilded server | | DISCORD_CLIENT_ID | All | | DISCORD_CLIENT_SECRET | those | | GOOGLE_CLIENT_ID | values | diff --git a/deployment/example-ecosystem.yml b/deployment/example-ecosystem.yml index 3823925..1a0fbbb 100644 --- a/deployment/example-ecosystem.yml +++ b/deployment/example-ecosystem.yml @@ -3,9 +3,9 @@ apps: name : 'web' node_args: --nouse-idle-notification --expose-gc env: - HOSTURL: "http://localhost" - ASSET_SERVER: "http://localhost" PORT: 80 + HOST: "localhost" + ASSET_SERVER: "http://localhost" REDIS_URL: 'redis://localhost:6379' MYSQL_HOST: "localhost" MYSQL_USER: "pixelplanet" diff --git a/package-lock.json b/package-lock.json index ec5e682..98bbaa0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7011,6 +7011,14 @@ "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true }, + "opentype.js": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.7.3.tgz", + "integrity": "sha1-QPuM4Yv9YOdESO/f5EKDQJg5eqs=", + "requires": { + "tiny-inflate": "^1.0.2" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -7526,6 +7534,14 @@ "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", "dev": true }, + "ppfun-captcha": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/ppfun-captcha/-/ppfun-captcha-1.6.2.tgz", + "integrity": "sha512-pe15Inrr791duH9mMAyc9PCiYdMY5kLbaLCuoWVLWJ+kydHvMnwnYPKQUfEXzohrGWidKkNKQJk3E9o1U3GdAg==", + "requires": { + "opentype.js": "^0.7.3" + } + }, "prebuild-install": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.0.0.tgz", @@ -9365,6 +9381,11 @@ "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", "dev": true }, + "tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, "tiny-invariant": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", diff --git a/package.json b/package.json index bdc3d25..4dbea75 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "passport-json": "^1.2.0", "passport-reddit": "^0.2.4", "passport-vkontakte": "^0.5.0", + "ppfun-captcha": "^1.6.2", "react": "^17.0.1", "react-dom": "^17.0.1", "react-icons": "^4.1.0", diff --git a/src/captchaserver.js b/src/captchaserver.js index 2266002..3fb970e 100644 --- a/src/captchaserver.js +++ b/src/captchaserver.js @@ -16,7 +16,8 @@ const PORT = 7000; const server = http.createServer((req, res) => { const captcha = ppfunCaptcha.create(); - console.log(`Serving ${captcha.text} to ${req.headers['x-real-ip']}`); + const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; + console.log(`Serving ${captcha.text} to ${ip}`); res.writeHead(200, { 'Content-Type': 'text/html', 'Cache-Control': 'no-cache', @@ -25,6 +26,6 @@ const server = http.createServer((req, res) => { res.end(); }); -server.listen(port, () => { - console.log(`Captcha Server listening on port ${port}`); +server.listen(PORT, () => { + console.log(`Captcha Server listening on port ${PORT}`); }); diff --git a/src/core/config.js b/src/core/config.js index fb6eb6c..1160bf3 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -35,8 +35,6 @@ export const MYSQL_USER = process.env.MYSQL_USER || 'pixelplanet'; export const MYSQL_PW = process.env.MYSQL_PW || 'password'; // Social -export const DISCORD_INVITE = process.env.DISCORD_INVITE - || 'https://discordapp.com/'; export const GUILDED_INVITE = process.env.GUILDED_INVITE || 'https://www.guilded.gg/'; diff --git a/src/utils/cloudflareip.js b/src/utils/cloudflareip.js deleted file mode 100644 index da95b2e..0000000 --- a/src/utils/cloudflareip.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * check if IP is from cloudflare - * @flow - */ - -import { Address4, Address6 } from 'ip-address'; - -import logger from '../core/logger'; - -// returns null | Address4 | Address6 -function intoAddress(str) { - if (typeof str === 'string') str = str.trim(); - try { - const isV6 = (str.indexOf(':') !== -1); - let ip = null; - if (isV6) { - ip = new Address6(str); - } else { - ip = new Address4(str); - } - return ip; - } catch { - logger.error(`CF-check: Got invalid ip ${str}`); - return null; - } -} - -const cloudflareIps = [ - '103.21.244.0/22', - '103.22.200.0/22', - '103.31.4.0/22', - '104.16.0.0/12', - '108.162.192.0/18', - '131.0.72.0/22', - '141.101.64.0/18', - '162.158.0.0/15', - '172.64.0.0/13', - '173.245.48.0/20', - '188.114.96.0/20', - '190.93.240.0/20', - '197.234.240.0/22', - '198.41.128.0/17', - '2400:cb00::/32', - '2405:8100::/32', - '2405:b500::/32', - '2606:4700::/32', - '2803:f800::/32', - '2c0f:f248::/32', - '2a06:98c0::/29', -].map(intoAddress); - -// returns bool -function isCloudflareIp(testIpString: string): boolean { - if (!testIpString) return false; - const testIp = intoAddress(testIpString); - if (!testIp) return false; - return cloudflareIps.some((cf) => testIp.isInSubnet(cf)); -} - -export default isCloudflareIp; diff --git a/src/utils/ip.js b/src/utils/ip.js index 2037d16..236bb29 100644 --- a/src/utils/ip.js +++ b/src/utils/ip.js @@ -3,20 +3,11 @@ * @flow */ -import isCloudflareIp from './cloudflareip'; - import logger from '../core/logger'; import { USE_XREALIP } from '../core/config'; -function isTrustedProxy(ip: string): boolean { - if (ip === '::ffff:127.0.0.1' || ip === '127.0.0.1' || isCloudflareIp(ip)) { - return true; - } - return false; -} - export function getHostFromRequest(req): ?string { const { headers } = req; const host = headers['x-forwarded-host'] || headers.host; @@ -26,30 +17,25 @@ export function getHostFromRequest(req): ?string { } export function getIPFromRequest(req): ?string { - const { socket, connection, headers } = req; + + if (USE_XREALIP) { + const ip = req.headers['x-real-ip']; + if (ip) { + return ip; + } + } + + const { socket, connection } = req; let conip = (connection ? connection.remoteAddress : socket.remoteAddress); conip = conip || '0.0.0.1'; - if (USE_XREALIP) { - const ip = headers['x-real-ip']; - return ip || conip; - } + // eslint-disable-next-line max-len + logger.warn( + `Connection not going through nginx and cloudflare! IP: ${conip}`, headers + ); - if (!headers['x-forwarded-for'] || !isTrustedProxy(conip)) { - // eslint-disable-next-line max-len - logger.warn(`Connection not going through nginx and cloudflare! IP: ${conip}`, headers); - return conip; - } - - const forwardedFor = headers['x-forwarded-for']; - const ipList = forwardedFor.split(',').map((str) => str.trim()); - - let ip = ipList.pop(); - while (isTrustedProxy(ip) && ipList.length) { - ip = ipList.pop(); - } - return ip || conip; + return conip; } export function getIPv6Subnet(ip: string): string { diff --git a/src/web.js b/src/web.js index 60f96fc..055c246 100644 --- a/src/web.js +++ b/src/web.js @@ -31,7 +31,7 @@ import generateGlobePage from './ssr-components/Globe'; import generateMainPage from './ssr-components/Main'; import { SECOND, MONTH } from './core/constants'; -import { PORT, DISCORD_INVITE, GUILDED_INVITE } from './core/config'; +import { PORT, HOST, GUILDED_INVITE } from './core/config'; import { ccToCoords } from './utils/location'; import { startAllCanvasLoops } from './core/tileserver'; @@ -111,11 +111,8 @@ app.use(express.static(path.join(__dirname, 'public'), { // -// Redirecct to discord and guilded +// Redirecct to guilded // ----------------------------------------------------------------------------- -app.use('/discord', (req, res) => { - res.redirect(DISCORD_INVITE); -}); app.use('/guilded', (req, res) => { res.redirect(GUILDED_INVITE); }); @@ -203,10 +200,13 @@ const promise = models.sync({ alter: { drop: false } }) // const promise = models.sync() .catch((err) => logger.error(err.stack)); promise.then(() => { - server.listen(PORT, () => { + server.listen(PORT, HOST, () => { rankings.updateRanking(); chatProvider.initialize(); const address = server.address(); - logger.log('info', `web is running at http://localhost:${address.port}/`); + logger.log( + 'info', + `web is running at http://${address.host}:${address.port}/`, + ); }); }); From dec817d8a001b210c49e2f3ea0f2466a5e41e44b Mon Sep 17 00:00:00 2001 From: HF Date: Fri, 5 Feb 2021 00:29:47 +0100 Subject: [PATCH 03/32] add ecosystem for captchas --- deployment/example-ecosystem-captchas.yml | 8 + i18n/template-ssr.pot | 48 +-- i18n/template.pot | 471 +++++++++++----------- scripts/copy.js | 4 + src/captchaserver.js | 26 +- src/core/config.js | 1 + src/utils/ip.js | 10 +- src/web.js | 2 +- webpack.config.web.babel.js | 1 + 9 files changed, 298 insertions(+), 273 deletions(-) create mode 100644 deployment/example-ecosystem-captchas.yml diff --git a/deployment/example-ecosystem-captchas.yml b/deployment/example-ecosystem-captchas.yml new file mode 100644 index 0000000..107fd8b --- /dev/null +++ b/deployment/example-ecosystem-captchas.yml @@ -0,0 +1,8 @@ +apps: + - script : ./captchaserver.js + name : 'captchas' + node_args: --nouse-idle-notification --expose-gc + env: + PORT: 80 + HOST: "localhost" + REDIS_URL: 'redis://localhost:6379' diff --git a/i18n/template-ssr.pot b/i18n/template-ssr.pot index 2e7c59a..be7cda7 100644 --- a/i18n/template-ssr.pot +++ b/i18n/template-ssr.pot @@ -57,11 +57,11 @@ msgstr "" msgid "Stop flooding." msgstr "" -#: src/ssr-components/Main.jsx:53 +#: src/ssr-components/Main.jsx:72 msgid "PixelPlanet.fun" msgstr "" -#: src/ssr-components/Main.jsx:55 +#: src/ssr-components/Main.jsx:74 msgid "Place color pixels on an map styled canvas with other players online" msgstr "" @@ -81,31 +81,31 @@ msgstr "" msgid "A 3D globe of our whole map" msgstr "" -#: src/routes/reset_password.js:65 +#: src/routes/reset_password.js:58 msgid "You sent an empty password or invalid data :(" msgstr "" -#: src/routes/reset_password.js:77 +#: src/routes/reset_password.js:70 msgid "This password-reset link isn't valid anymore :(" msgstr "" -#: src/routes/reset_password.js:88 +#: src/routes/reset_password.js:81 msgid "Your passwords do not match :(" msgstr "" -#: src/routes/reset_password.js:103 +#: src/routes/reset_password.js:96 msgid "User doesn't exist in our database :(" msgstr "" -#: src/routes/reset_password.js:115 +#: src/routes/reset_password.js:108 msgid "Passowrd successfully changed." msgstr "" -#: src/routes/reset_password.js:134 +#: src/routes/reset_password.js:127 msgid "Invalid url :( Please check your mail again." msgstr "" -#: src/routes/reset_password.js:147 +#: src/routes/reset_password.js:140 msgid "" "This passwort reset link is wrong or already expired, please request a new " "one (Note: you can use those links just once)" @@ -290,8 +290,19 @@ msgstr "" msgid "Incorrect password!" msgstr "" -#: src/routes/api/auth/logout.js:13 -msgid "You are not even logged in." +#: src/routes/api/auth/verify.js:25 +#: src/routes/api/auth/verify.js:32 +msgid "Mail verification" +msgstr "" + +#: src/routes/api/auth/verify.js:26 +msgid "You are now verified :)" +msgstr "" + +#: src/routes/api/auth/verify.js:32 +msgid "" +"Your mail verification code is invalid or already expired :(, please " +"request a new one." msgstr "" #: src/routes/api/auth/register.js:31 @@ -310,19 +321,8 @@ msgstr "" msgid "Failed to establish session after register :(" msgstr "" -#: src/routes/api/auth/verify.js:25 -#: src/routes/api/auth/verify.js:32 -msgid "Mail verification" -msgstr "" - -#: src/routes/api/auth/verify.js:26 -msgid "You are now verified :)" -msgstr "" - -#: src/routes/api/auth/verify.js:32 -msgid "" -"Your mail verification code is invalid or already expired :(, please " -"request a new one." +#: src/routes/api/auth/logout.js:13 +msgid "You are not even logged in." msgstr "" #: src/ssr-components/RedirectionPage.jsx:20 diff --git a/i18n/template.pot b/i18n/template.pot index 5b3f5d7..298cc33 100644 --- a/i18n/template.pot +++ b/i18n/template.pot @@ -126,80 +126,14 @@ msgstr "" msgid "Pixels placed" msgstr "" -#: src/components/CanvasSelectModal.jsx:67 -#: src/components/CanvasSwitchButton.jsx:20 -msgid "Canvas Selection" -msgstr "" - #: src/components/Converter.jsx:609 #: src/components/CoordinatesBox.jsx:26 msgid "Copy to Clipboard" msgstr "" -#: src/components/ChannelContextMenu.jsx:73 -msgid "Mute" -msgstr "" - -#: src/components/UserContextMenu.jsx:72 -msgid "Ping" -msgstr "" - -#: src/components/UserContextMenu.jsx:97 -msgid "DM" -msgstr "" - -#: src/components/UserContextMenu.jsx:107 -msgid "Block" -msgstr "" - -#: src/components/PalselButton.jsx:25 -msgid "Close Palette" -msgstr "" - -#: src/components/PalselButton.jsx:25 -msgid "Open Palette" -msgstr "" - -#: src/components/GlobeButton.jsx:31 -msgid "Globe View" -msgstr "" - -#: src/components/SettingsButton.jsx:20 -#: src/components/SettingsModal.jsx:267 -msgid "Settings" -msgstr "" - -#: src/components/LogInButton.jsx:20 -#: src/components/UserAreaModal.jsx:160 -msgid "User Area" -msgstr "" - -#: src/components/HelpButton.jsx:20 -msgid "Help" -msgstr "" - -#: src/components/DownloadButton.jsx:37 -msgid "Make Screenshot" -msgstr "" - -#: src/components/Chat.jsx:143 -msgid "Channel settings" -msgstr "" - -#: src/components/Chat.jsx:152 -msgid "maximize" -msgstr "" - -#: src/components/Chat.jsx:168 -msgid "Start chatting here" -msgstr "" - -#: src/components/Chat.jsx:200 -msgid "Chat here" -msgstr "" - -#: src/components/Chat.jsx:220 -msgid "You must be logged in to chat" +#: src/components/CanvasSelectModal.jsx:67 +#: src/components/CanvasSwitchButton.jsx:20 +msgid "Canvas Selection" msgstr "" #: src/components/ForgotPasswordModal.jsx:16 @@ -216,6 +150,10 @@ msgstr "" msgid "Restore my Password" msgstr "" +#: src/components/ChatModal.jsx:35 +msgid "Chat" +msgstr "" + #: src/components/ArchiveModal.jsx:20 msgid "" "While we tend to not delete canvases, some canvases are started for fun or " @@ -253,18 +191,6 @@ msgstr "" msgid "Canvas Archive" msgstr "" -#: src/components/ChatModal.jsx:35 -msgid "Chat" -msgstr "" - -#: src/components/RegisterModal.jsx:18 -msgid "Register new account here" -msgstr "" - -#: src/components/RegisterModal.jsx:38 -msgid "Register New Account" -msgstr "" - #: src/components/CanvasSelectModal.jsx:29 msgid "" "Select the canvas you want to use. Every canvas is unique and has different " @@ -324,6 +250,111 @@ msgstr "" msgid "Loading..." msgstr "" +#: src/components/LogInButton.jsx:20 +#: src/components/UserAreaModal.jsx:160 +msgid "User Area" +msgstr "" + +#: src/components/RegisterModal.jsx:18 +msgid "Register new account here" +msgstr "" + +#: src/components/RegisterModal.jsx:38 +msgid "Register New Account" +msgstr "" + +#: src/components/SettingsModal.jsx:125 +msgid "Show Grid" +msgstr "" + +#: src/components/SettingsModal.jsx:126 +msgid "Turn on grid to highlight pixel borders." +msgstr "" + +#: src/components/SettingsModal.jsx:132 +msgid "Show Pixel Activity" +msgstr "" + +#: src/components/SettingsModal.jsx:133 +msgid "Show circles where pixels are placed." +msgstr "" + +#: src/components/SettingsModal.jsx:139 +msgid "Disable Game Sounds" +msgstr "" + +#: src/components/SettingsModal.jsx:141 +msgid "All sound effects will be disabled." +msgstr "" + +#: src/components/SettingsModal.jsx:147 +msgid "Enable chat notifications" +msgstr "" + +#: src/components/SettingsModal.jsx:148 +msgid "Play a sound when new chat messages arrive" +msgstr "" + +#: src/components/SettingsModal.jsx:153 +msgid "Auto Zoom In" +msgstr "" + +#: src/components/SettingsModal.jsx:155 +msgid "" +"Zoom in instead of placing a pixel when you tap the canvas and your zoom is " +"small." +msgstr "" + +#: src/components/SettingsModal.jsx:160 +msgid "Compact Palette" +msgstr "" + +#: src/components/SettingsModal.jsx:162 +msgid "Display Palette in a compact form that takes less screen space." +msgstr "" + +#: src/components/SettingsModal.jsx:167 +msgid "Potato Mode" +msgstr "" + +#: src/components/SettingsModal.jsx:168 +msgid "For when you are playing on a potato." +msgstr "" + +#: src/components/Converter.jsx:423 +#: src/components/SettingsModal.jsx:173 +msgid "Light Grid" +msgstr "" + +#: src/components/SettingsModal.jsx:174 +msgid "Show Grid in white instead of black." +msgstr "" + +#: src/components/SettingsModal.jsx:180 +msgid "Historical View" +msgstr "" + +#: src/components/SettingsModal.jsx:181 +msgid "Check out past versions of the canvas." +msgstr "" + +#: src/components/SettingsModal.jsx:189 +msgid "Themes" +msgstr "" + +#: src/components/SettingsModal.jsx:190 +msgid "How pixelplanet should look like." +msgstr "" + +#: src/components/SettingsModal.jsx:200 +msgid "Select Language" +msgstr "" + +#: src/components/SettingsButton.jsx:20 +#: src/components/SettingsModal.jsx:278 +msgid "Settings" +msgstr "" + #: src/components/HelpModal.jsx:34 #: src/components/HelpModal.jsx:35 msgid "Privacy Policy" @@ -517,140 +548,60 @@ msgstr "" msgid "Welcome to PixelPlanet.fun" msgstr "" -#: src/components/SettingsModal.jsx:123 -msgid "Show Grid" +#: src/components/UserContextMenu.jsx:72 +msgid "Ping" msgstr "" -#: src/components/SettingsModal.jsx:124 -msgid "Turn on grid to highlight pixel borders." +#: src/components/UserContextMenu.jsx:97 +msgid "DM" msgstr "" -#: src/components/SettingsModal.jsx:130 -msgid "Show Pixel Activity" +#: src/components/UserContextMenu.jsx:107 +msgid "Block" msgstr "" -#: src/components/SettingsModal.jsx:131 -msgid "Show circles where pixels are placed." +#: src/components/ChannelContextMenu.jsx:73 +msgid "Mute" msgstr "" -#: src/components/SettingsModal.jsx:137 -msgid "Disable Game Sounds" +#: src/components/PalselButton.jsx:25 +msgid "Close Palette" msgstr "" -#: src/components/SettingsModal.jsx:139 -msgid "All sound effects will be disabled." +#: src/components/PalselButton.jsx:25 +msgid "Open Palette" msgstr "" -#: src/components/SettingsModal.jsx:145 -msgid "Enable chat notifications" +#: src/components/GlobeButton.jsx:31 +msgid "Globe View" msgstr "" -#: src/components/SettingsModal.jsx:146 -msgid "Play a sound when new chat messages arrive" +#: src/components/Chat.jsx:143 +msgid "Channel settings" msgstr "" -#: src/components/SettingsModal.jsx:151 -msgid "Auto Zoom In" +#: src/components/Chat.jsx:152 +msgid "maximize" msgstr "" -#: src/components/SettingsModal.jsx:153 -msgid "" -"Zoom in instead of placing a pixel when you tap the canvas and your zoom is " -"small." +#: src/components/Chat.jsx:168 +msgid "Start chatting here" msgstr "" -#: src/components/SettingsModal.jsx:158 -msgid "Compact Palette" +#: src/components/Chat.jsx:200 +msgid "Chat here" msgstr "" -#: src/components/SettingsModal.jsx:160 -msgid "Display Palette in a compact form that takes less screen space." +#: src/components/Chat.jsx:220 +msgid "You must be logged in to chat" msgstr "" -#: src/components/SettingsModal.jsx:165 -msgid "Potato Mode" +#: src/components/DownloadButton.jsx:37 +msgid "Make Screenshot" msgstr "" -#: src/components/SettingsModal.jsx:166 -msgid "For when you are playing on a potato." -msgstr "" - -#: src/components/Converter.jsx:423 -#: src/components/SettingsModal.jsx:171 -msgid "Light Grid" -msgstr "" - -#: src/components/SettingsModal.jsx:172 -msgid "Show Grid in white instead of black." -msgstr "" - -#: src/components/SettingsModal.jsx:179 -msgid "Historical View" -msgstr "" - -#: src/components/SettingsModal.jsx:180 -msgid "Check out past versions of the canvas." -msgstr "" - -#: src/components/SettingsModal.jsx:188 -msgid "Themes" -msgstr "" - -#: src/components/SettingsModal.jsx:189 -msgid "How pixelplanet should look like." -msgstr "" - -#: src/components/ChangeMail.jsx:104 -#: src/components/ChangeName.jsx:82 -#: src/components/ChangePassword.jsx:120 -#: src/components/DeleteAccount.jsx:82 -#: src/components/LogInForm.jsx:97 -#: src/components/NewPasswordForm.jsx:93 -#: src/components/SignUpForm.jsx:118 -msgid "Error" -msgstr "" - -#: src/components/SignUpForm.jsx:126 -msgid "Name" -msgstr "" - -#: src/components/NewPasswordForm.jsx:100 -#: src/components/SignUpForm.jsx:133 -msgid "Email" -msgstr "" - -#: src/components/ChangeMail.jsx:112 -#: src/components/DeleteAccount.jsx:89 -#: src/components/LogInForm.jsx:111 -#: src/components/SignUpForm.jsx:140 -msgid "Password" -msgstr "" - -#: src/components/SignUpForm.jsx:149 -msgid "Confirm Password" -msgstr "" - -#: src/components/Admintools.jsx:306 -#: src/components/Admintools.jsx:387 -#: src/components/Admintools.jsx:461 -#: src/components/Admintools.jsx:505 -#: src/components/Admintools.jsx:589 -#: src/components/NewPasswordForm.jsx:104 -#: src/components/SignUpForm.jsx:152 -msgid "Submit" -msgstr "" - -#: src/components/ChangeMail.jsx:125 -#: src/components/ChangeName.jsx:94 -#: src/components/ChangePassword.jsx:152 -#: src/components/DeleteAccount.jsx:95 -#: src/components/NewPasswordForm.jsx:106 -#: src/components/SignUpForm.jsx:158 -msgid "Cancel" -msgstr "" - -#: src/components/NewPasswordForm.jsx:83 -msgid "Sent you a mail with instructions to reset your password." +#: src/components/HelpButton.jsx:20 +msgid "Help" msgstr "" #: src/components/CanvasItem.jsx:46 @@ -694,14 +645,6 @@ msgstr "" msgid "Ranking updates every 5 min. Daily rankings get reset at midnight UTC." msgstr "" -#: src/components/LogInForm.jsx:104 -msgid "Name or Email" -msgstr "" - -#: src/components/LogInForm.jsx:115 -msgid "LogIn" -msgstr "" - #: src/components/UserArea.jsx:57 msgid "Todays Placed Pixels" msgstr "" @@ -747,6 +690,70 @@ msgstr "" msgid "Social Settings" msgstr "" +#: src/components/ChangeMail.jsx:104 +#: src/components/ChangeName.jsx:82 +#: src/components/ChangePassword.jsx:120 +#: src/components/DeleteAccount.jsx:82 +#: src/components/LogInForm.jsx:97 +#: src/components/NewPasswordForm.jsx:93 +#: src/components/SignUpForm.jsx:118 +msgid "Error" +msgstr "" + +#: src/components/LogInForm.jsx:104 +msgid "Name or Email" +msgstr "" + +#: src/components/ChangeMail.jsx:112 +#: src/components/DeleteAccount.jsx:89 +#: src/components/LogInForm.jsx:111 +#: src/components/SignUpForm.jsx:140 +msgid "Password" +msgstr "" + +#: src/components/LogInForm.jsx:115 +msgid "LogIn" +msgstr "" + +#: src/components/SignUpForm.jsx:126 +msgid "Name" +msgstr "" + +#: src/components/NewPasswordForm.jsx:100 +#: src/components/SignUpForm.jsx:133 +msgid "Email" +msgstr "" + +#: src/components/SignUpForm.jsx:149 +msgid "Confirm Password" +msgstr "" + +#: src/components/Admintools.jsx:306 +#: src/components/Admintools.jsx:387 +#: src/components/Admintools.jsx:461 +#: src/components/Admintools.jsx:505 +#: src/components/Admintools.jsx:589 +#: src/components/NewPasswordForm.jsx:104 +#: src/components/SignUpForm.jsx:152 +msgid "Submit" +msgstr "" + +#: src/components/ChangeMail.jsx:125 +#: src/components/ChangeName.jsx:94 +#: src/components/ChangePassword.jsx:152 +#: src/components/DeleteAccount.jsx:95 +#: src/components/NewPasswordForm.jsx:106 +#: src/components/SignUpForm.jsx:158 +msgid "Cancel" +msgstr "" + +#: src/components/ChangeMail.jsx:123 +#: src/components/ChangeName.jsx:92 +#: src/components/ChangePassword.jsx:150 +#: src/components/LanguageSelect.jsx:73 +msgid "Save" +msgstr "" + #: src/components/Admintools.jsx:179 msgid "Build image on canvas." msgstr "" @@ -825,6 +832,10 @@ msgstr "" msgid "User Name" msgstr "" +#: src/components/NewPasswordForm.jsx:83 +msgid "Sent you a mail with instructions to reset your password." +msgstr "" + #: src/components/Converter.jsx:274 msgid "Choose Canvas" msgstr "" @@ -964,10 +975,6 @@ msgstr "" msgid "You have no users blocked" msgstr "" -#: src/components/DeleteAccount.jsx:93 -msgid "Yes, Delete My Account!" -msgstr "" - #: src/components/ChangeMail.jsx:89 msgid "" "Changed Mail successfully. We sent you a verification mail, " @@ -978,36 +985,14 @@ msgstr "" msgid "New Mail" msgstr "" -#: src/components/ChangeMail.jsx:123 -#: src/components/ChangeName.jsx:92 -#: src/components/ChangePassword.jsx:150 -msgid "Save" +#: src/components/DeleteAccount.jsx:93 +msgid "Yes, Delete My Account!" msgstr "" #: src/components/ChangeName.jsx:88 msgid "New Username" msgstr "" -#: src/components/ChangePassword.jsx:18 -msgid "Passwords do not match." -msgstr "" - -#: src/components/ChangePassword.jsx:103 -msgid "Changed Password successfully." -msgstr "" - -#: src/components/ChangePassword.jsx:129 -msgid "Old Password" -msgstr "" - -#: src/components/ChangePassword.jsx:137 -msgid "New Password" -msgstr "" - -#: src/components/ChangePassword.jsx:146 -msgid "Confirm New Password" -msgstr "" - #: src/components/UserMessages.jsx:41 msgid "A new verification mail is getting sent to you." msgstr "" @@ -1043,20 +1028,45 @@ msgstr "" msgid "Deny" msgstr "" +#: src/components/ChangePassword.jsx:18 +msgid "Passwords do not match." +msgstr "" + +#: src/components/ChangePassword.jsx:103 +msgid "Changed Password successfully." +msgstr "" + +#: src/components/ChangePassword.jsx:129 +msgid "Old Password" +msgstr "" + +#: src/components/ChangePassword.jsx:137 +msgid "New Password" +msgstr "" + +#: src/components/ChangePassword.jsx:146 +msgid "Confirm New Password" +msgstr "" + #: src/components/HelpModal.jsx:15 -#: src/components/SettingsModal.jsx:125 +#: src/components/SettingsModal.jsx:127 msgctxt "keybinds" msgid "G" msgstr "" #: src/components/HelpModal.jsx:16 -#: src/components/SettingsModal.jsx:132 +#: src/components/SettingsModal.jsx:134 msgctxt "keybinds" msgid "X" msgstr "" +#: src/components/SettingsModal.jsx:142 +msgctxt "keybinds" +msgid "M" +msgstr "" + #: src/components/HelpModal.jsx:17 -#: src/components/SettingsModal.jsx:182 +#: src/components/SettingsModal.jsx:183 msgctxt "keybinds" msgid "H" msgstr "" @@ -1104,9 +1114,4 @@ msgstr "" #: src/components/HelpModal.jsx:32 msgctxt "keybinds" msgid "C" -msgstr "" - -#: src/components/SettingsModal.jsx:140 -msgctxt "keybinds" -msgid "M" msgstr "" \ No newline at end of file diff --git a/scripts/copy.js b/scripts/copy.js index 3e2b01f..9617a75 100644 --- a/scripts/copy.js +++ b/scripts/copy.js @@ -92,6 +92,10 @@ async function copy() { `${deploydir}/example-ecosystem-backup.yml`, `${builddir}/ecosystem-backup.example.yml`, ), + copyFile( + `${deploydir}/example-ecosystem-captchas.yml`, + `${builddir}/ecosystem-captchas.example.yml`, + ), ]); } diff --git a/src/captchaserver.js b/src/captchaserver.js index 3fb970e..5ba5a45 100644 --- a/src/captchaserver.js +++ b/src/captchaserver.js @@ -2,20 +2,26 @@ * serving captchas */ +/* eslint-disable no-console */ + import process from 'process'; -import http from 'http'; +import http from 'http'; import ppfunCaptcha from 'ppfun-captcha'; -/* -const [ - PORT, - REDIS_URL, -] = process.argv.slice(2); -*/ -const PORT = 7000; +const PORT = process.env.PORT || 80; +const HOST = process.env.HOST || 'localhost'; const server = http.createServer((req, res) => { - const captcha = ppfunCaptcha.create(); + const captcha = ppfunCaptcha.create({ + width: 700, + height: 500, + fontSize: 600, + stroke: 'black', + fill: 'none', + background: 'white', + nodeDeviation: 0.5, + connectionPathDeviation: 0.3, + }); const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; console.log(`Serving ${captcha.text} to ${ip}`); res.writeHead(200, { @@ -26,6 +32,6 @@ const server = http.createServer((req, res) => { res.end(); }); -server.listen(PORT, () => { +server.listen(PORT, HOST, () => { console.log(`Captcha Server listening on port ${PORT}`); }); diff --git a/src/core/config.js b/src/core/config.js index 1160bf3..b807ac6 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -10,6 +10,7 @@ if (process.env.BROWSER) { } export const PORT = process.env.PORT || 80; +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; diff --git a/src/utils/ip.js b/src/utils/ip.js index 236bb29..2226e1f 100644 --- a/src/utils/ip.js +++ b/src/utils/ip.js @@ -17,7 +17,6 @@ export function getHostFromRequest(req): ?string { } export function getIPFromRequest(req): ?string { - if (USE_XREALIP) { const ip = req.headers['x-real-ip']; if (ip) { @@ -30,10 +29,11 @@ export function getIPFromRequest(req): ?string { let conip = (connection ? connection.remoteAddress : socket.remoteAddress); conip = conip || '0.0.0.1'; - // eslint-disable-next-line max-len - logger.warn( - `Connection not going through nginx and cloudflare! IP: ${conip}`, headers - ); + if (!USE_XREALIP) { + logger.warn( + `Connection not going through reverse proxy! IP: ${conip}`, reqheaders, + ); + } return conip; } diff --git a/src/web.js b/src/web.js index 055c246..0989df9 100644 --- a/src/web.js +++ b/src/web.js @@ -205,7 +205,7 @@ promise.then(() => { chatProvider.initialize(); const address = server.address(); logger.log( - 'info', + 'info', `web is running at http://${address.host}:${address.port}/`, ); }); diff --git a/webpack.config.web.babel.js b/webpack.config.web.babel.js index cf8a24e..0f4cb6d 100644 --- a/webpack.config.web.babel.js +++ b/webpack.config.web.babel.js @@ -64,6 +64,7 @@ export default ({ entry: { web: [path.resolve(__dirname, 'src', 'web.js')], backup: [path.resolve(__dirname, 'src', 'backup.js')], + captchaserver: [path.resolve(__dirname, 'src', 'captchaserver.js')], }, output: { From e76e56027f60deb3357f7558f17631a9a56a1236 Mon Sep 17 00:00:00 2001 From: HF Date: Sat, 6 Feb 2021 11:54:03 +0100 Subject: [PATCH 04/32] split ranks from user reducer --- src/components/DailyRankings.jsx | 2 +- src/components/TotalRankings.jsx | 2 +- src/components/UserArea.jsx | 4 +- src/reducers/index.js | 3 ++ src/reducers/ranks.js | 80 ++++++++++++++++++++++++++++++++ src/reducers/user.js | 50 -------------------- src/utils/ip.js | 2 +- 7 files changed, 89 insertions(+), 54 deletions(-) create mode 100644 src/reducers/ranks.js diff --git a/src/components/DailyRankings.jsx b/src/components/DailyRankings.jsx index 672519c..80eb57c 100644 --- a/src/components/DailyRankings.jsx +++ b/src/components/DailyRankings.jsx @@ -34,7 +34,7 @@ const DailyRankings = ({ totalDailyRanking }) => ( ); function mapStateToProps(state: State) { - const { totalDailyRanking } = state.user; + const { totalDailyRanking } = state.ranks; return { totalDailyRanking }; } diff --git a/src/components/TotalRankings.jsx b/src/components/TotalRankings.jsx index a227f90..10bbd9c 100644 --- a/src/components/TotalRankings.jsx +++ b/src/components/TotalRankings.jsx @@ -34,7 +34,7 @@ const TotalRankings = ({ totalRanking }) => ( ); function mapStateToProps(state: State) { - const { totalRanking } = state.user; + const { totalRanking } = state.ranks; return { totalRanking }; } diff --git a/src/components/UserArea.jsx b/src/components/UserArea.jsx index a11f0a1..28c5ada 100644 --- a/src/components/UserArea.jsx +++ b/src/components/UserArea.jsx @@ -212,11 +212,13 @@ function mapStateToProps(state: State) { const { name, mailreg, + } = state.user; + const { totalPixels, dailyTotalPixels, ranking, dailyRanking, - } = state.user; + } = state.ranks; const stats = { totalPixels, dailyTotalPixels, diff --git a/src/reducers/index.js b/src/reducers/index.js index 28fc39e..0ad4c09 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -7,6 +7,7 @@ import canvas from './canvas'; import gui from './gui'; import modal from './modal'; import user from './user'; +import ranks from './ranks'; import chat from './chat'; import contextMenu from './contextMenu'; import chatRead from './chatRead'; @@ -38,6 +39,7 @@ const config = { storage: localForage, blacklist: [ 'user', + 'ranks', 'canvas', 'modal', 'chat', @@ -52,6 +54,7 @@ export default persistCombineReducers(config, { gui, modal, user, + ranks, chat, contextMenu, chatRead, diff --git a/src/reducers/ranks.js b/src/reducers/ranks.js new file mode 100644 index 0000000..fc87936 --- /dev/null +++ b/src/reducers/ranks.js @@ -0,0 +1,80 @@ +/* @flow */ + +import type { Action } from '../actions/types'; + +export type UserState = { + totalPixels: number, + dailyTotalPixels: number, + ranking: number, + dailyRanking: number, + // global stats + online: ?number, + totalRanking: Object, + totalDailyRanking: Object, +}; + +const initialState: UserState = { + totalPixels: 0, + dailyTotalPixels: 0, + ranking: 1488, + dailyRanking: 1488, + online: 1, + totalRanking: {}, + totalDailyRanking: {}, +}; + +export default function ranks( + state: UserState = initialState, + action: Action, +): UserState { + switch (action.type) { + case 'PLACED_PIXELS': { + let { totalPixels, dailyTotalPixels } = state; + const { amount } = action; + totalPixels += amount; + dailyTotalPixels += amount; + return { + ...state, + totalPixels, + dailyTotalPixels, + }; + } + + case 'RECEIVE_ONLINE': { + const { online } = action; + return { + ...state, + online, + }; + } + + case 'RECEIVE_ME': + case 'LOGIN': { + const { + totalPixels, + dailyTotalPixels, + ranking, + dailyRanking, + } = action; + return { + ...state, + totalPixels, + dailyTotalPixels, + ranking, + dailyRanking, + }; + } + + case 'RECEIVE_STATS': { + const { totalRanking, totalDailyRanking } = action; + return { + ...state, + totalRanking, + totalDailyRanking, + }; + } + + default: + return state; + } +} diff --git a/src/reducers/user.js b/src/reducers/user.js index 067261d..37a5a08 100644 --- a/src/reducers/user.js +++ b/src/reducers/user.js @@ -5,7 +5,6 @@ import type { Action } from '../actions/types'; import { createNameRegExp } from '../core/utils'; - export type UserState = { name: string, center: Cell, @@ -13,18 +12,9 @@ export type UserState = { coolDown: ?number, // ms lastCoolDownEnd: ?Date, requestingPixel: boolean, - online: ?number, // messages are sent by api/me, like not_verified status messages: Array, mailreg: boolean, - // stats - totalPixels: number, - dailyTotalPixels: number, - ranking: number, - dailyRanking: number, - // global stats - totalRanking: Object, - totalDailyRanking: Object, // minecraft minecraftname: string, // blocking all Dms @@ -46,11 +36,8 @@ const initialState: UserState = { coolDown: null, lastCoolDownEnd: null, requestingPixel: true, - online: null, messages: [], mailreg: false, - totalRanking: {}, - totalDailyRanking: {}, minecraftname: null, blockDm: false, isOnMobile: false, @@ -120,35 +107,11 @@ export default function user( }; } - case 'PLACED_PIXELS': { - let { totalPixels, dailyTotalPixels } = state; - const { amount } = action; - totalPixels += amount; - dailyTotalPixels += amount; - return { - ...state, - totalPixels, - dailyTotalPixels, - }; - } - - case 'RECEIVE_ONLINE': { - const { online } = action; - return { - ...state, - online, - }; - } - case 'RECEIVE_ME': case 'LOGIN': { const { name, mailreg, - totalPixels, - dailyTotalPixels, - ranking, - dailyRanking, minecraftname, blockDm, userlvl, @@ -160,10 +123,6 @@ export default function user( name, messages, mailreg, - totalPixels, - dailyTotalPixels, - ranking, - dailyRanking, minecraftname, blockDm, userlvl, @@ -184,15 +143,6 @@ export default function user( }; } - case 'RECEIVE_STATS': { - const { totalRanking, totalDailyRanking } = action; - return { - ...state, - totalRanking, - totalDailyRanking, - }; - } - case 'SET_NAME': { const { name } = action; const nameRegExp = createNameRegExp(name); diff --git a/src/utils/ip.js b/src/utils/ip.js index 2226e1f..e7afe69 100644 --- a/src/utils/ip.js +++ b/src/utils/ip.js @@ -31,7 +31,7 @@ export function getIPFromRequest(req): ?string { if (!USE_XREALIP) { logger.warn( - `Connection not going through reverse proxy! IP: ${conip}`, reqheaders, + `Connection not going through reverse proxy! IP: ${conip}`, req.headers, ); } From ee21dd9d8658b4ee16b7222460fd2c7b703fc991 Mon Sep 17 00:00:00 2001 From: HF Date: Sun, 14 Feb 2021 22:23:36 +0100 Subject: [PATCH 05/32] captcha.jsx --- src/components/Captcha.jsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/components/Captcha.jsx diff --git a/src/components/Captcha.jsx b/src/components/Captcha.jsx new file mode 100644 index 0000000..220a461 --- /dev/null +++ b/src/components/Captcha.jsx @@ -0,0 +1,12 @@ +/* + * @flow + */ + +import React from 'react'; +import { t } from 'ttag'; + +const Captcha = ({ +}) => ( +
+
+); From 74b4a4422404d3d830b423a51e0b88a3c8f3f2e1 Mon Sep 17 00:00:00 2001 From: HF Date: Tue, 23 Feb 2021 11:37:52 +0100 Subject: [PATCH 06/32] replace sweetalert --- package-lock.json | 5 --- package.json | 2 +- src/actions/index.js | 6 ++++ src/actions/types.js | 1 + src/components/Alert.jsx | 70 +++++++++++++++++++++++++++++++++++++ src/components/Captcha.jsx | 3 +- src/components/ChatBox.jsx | 31 +++++----------- src/components/UI.jsx | 8 +++-- src/reducers/alert.js | 51 +++++++++++++++++++++++++++ src/reducers/index.js | 7 ++++ src/reducers/ranks.js | 8 ++--- src/store/configureStore.js | 2 -- src/store/sweetAlert.js | 30 ---------------- src/styles/default.css | 29 ++++++++------- 14 files changed, 172 insertions(+), 81 deletions(-) create mode 100644 src/components/Alert.jsx create mode 100644 src/reducers/alert.js delete mode 100644 src/store/sweetAlert.js diff --git a/package-lock.json b/package-lock.json index 98bbaa0..f25eee4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9074,11 +9074,6 @@ } } }, - "sweetalert2": { - "version": "10.14.0", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-10.14.0.tgz", - "integrity": "sha512-EBUh4k9qyRRsttm9X9j7WUhLExetvTJpcbp1VTMJCpuI2UwHLesXMIw9M+UeuqBywv0UjNMR5PKH7Qnv65m8rw==" - }, "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", diff --git a/package.json b/package.json index 4dbea75..e357aa7 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "main": "server.js", "scripts": { "build": "babel-node scripts/run prebuild && npm run webpack", + "build-en": "babel-node scripts/run prebuild && npm run extract", "clean": "babel-node scripts/run clean", "webpack": "webpack --config ./webpack.config.web.babel.js && parallel-webpack --config ./webpack.config.client.babel.js", "extract": "webpack --env extract --config ./webpack.config.web.babel.js && webpack --env extract --config ./webpack.config.client.babel.js", @@ -76,7 +77,6 @@ "sequelize": "^6.5.0", "sharp": "^0.27.1", "startaudiocontext": "^1.2.1", - "sweetalert2": "^10.14.0", "three": "^0.125.2", "three-trackballcontrols": "^0.9.0", "ttag": "^1.7.24", diff --git a/src/actions/index.js b/src/actions/index.js index 0eb8333..42cf06a 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -29,6 +29,12 @@ export function sweetAlert( }; } +export function closeAlert(): Action { + return { + type: 'CLOSE_ALERT', + }; +} + export function toggleChatBox(): Action { return { type: 'TOGGLE_CHAT_BOX', diff --git a/src/actions/types.js b/src/actions/types.js index 52b20a7..5b75cd4 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -14,6 +14,7 @@ export type Action = icon: string, confirmButtonText: string, } + | { type: 'CLOSE_ALERT' } | { type: 'TOGGLE_GRID' } | { type: 'TOGGLE_PIXEL_NOTIFY' } | { type: 'TOGGLE_AUTO_ZOOM_IN' } diff --git a/src/components/Alert.jsx b/src/components/Alert.jsx new file mode 100644 index 0000000..c18ee30 --- /dev/null +++ b/src/components/Alert.jsx @@ -0,0 +1,70 @@ +/* + * + * @flow + */ + +import React, { useState, useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; + +import { closeAlert } from '../actions'; + +const Alert = () => { + const [render, setRender] = useState(false); + + const { + alertOpen, + alertType, + alertTitle, + alertMessage, + alertBtn, + } = useSelector((state) => state.alert); + + const dispatch = useDispatch(); + const close = () => { + dispatch(closeAlert()); + }; + + const onTransitionEnd = () => { + if (!alertOpen) setRender(false); + }; + + useEffect(() => { + window.setTimeout(() => { + if (alertOpen) setRender(true); + }, 10); + }, [alertOpen]); + + return ( + (render || alertOpen) && ( +
+
+

{alertTitle}

+

+ {alertMessage} +

+

+ +

+
+
+ ) + ); +}; + +export default React.memo(Alert); diff --git a/src/components/Captcha.jsx b/src/components/Captcha.jsx index 220a461..78f97f8 100644 --- a/src/components/Captcha.jsx +++ b/src/components/Captcha.jsx @@ -7,6 +7,5 @@ import { t } from 'ttag'; const Captcha = ({ }) => ( -
-
+ div /> ); diff --git a/src/components/ChatBox.jsx b/src/components/ChatBox.jsx index b9adaf8..0fa3c12 100644 --- a/src/components/ChatBox.jsx +++ b/src/components/ChatBox.jsx @@ -4,34 +4,34 @@ */ import React, { useEffect, useState } from 'react'; -import { connect } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; -import type { State } from '../reducers'; import useWindowSize from '../utils/reactHookResize'; import { showChatModal } from '../actions'; import Chat from './Chat'; -function ChatBox({ - chatOpen, - triggerModal, -}) { +const ChatBox = () => { const [render, setRender] = useState(false); + const chatOpen = useSelector((state) => state.modal.chatOpen); + + const dispatch = useDispatch(); + useEffect(() => { window.setTimeout(() => { if (chatOpen) setRender(true); }, 10); }, [chatOpen]); - const onTransitionEnd = () => { + const onTransitionEnd =() => { if (!chatOpen) setRender(false); }; const { width } = useWindowSize(); if (width < 604 && chatOpen) { - triggerModal(); + dispatch(showChatModal(true)); } return ( @@ -46,17 +46,4 @@ function ChatBox({ ); } -function mapStateToProps(state: State) { - const { chatOpen } = state.modal; - return { chatOpen }; -} - -function mapDispatchToProps(dispatch) { - return { - triggerModal() { - dispatch(showChatModal(true)); - }, - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(ChatBox); +export default React.memo(ChatBox); diff --git a/src/components/UI.jsx b/src/components/UI.jsx index 2320d68..91b3783 100644 --- a/src/components/UI.jsx +++ b/src/components/UI.jsx @@ -12,6 +12,7 @@ import NotifyBox from './NotifyBox'; import GlobeButton from './GlobeButton'; import PalselButton from './PalselButton'; import Palette from './Palette'; +import Alert from './Alert'; import HistorySelect from './HistorySelect'; import Mobile3DControls from './Mobile3DControls'; import UserContextMenu from './UserContextMenu'; @@ -41,13 +42,14 @@ const UI = ({ } return (
+ - {(is3D) ? null : } - {(is3D && isOnMobile) ? : null} + {(!is3D) && } + {(is3D && isOnMobile) && } - {(menuOpen && menuType) ? CONTEXT_MENUS[menuType] : null} + {(menuOpen && menuType) && CONTEXT_MENUS[menuType]}
); }; diff --git a/src/reducers/alert.js b/src/reducers/alert.js new file mode 100644 index 0000000..30e7eda --- /dev/null +++ b/src/reducers/alert.js @@ -0,0 +1,51 @@ +/* @flow */ + +import type { Action } from '../actions/types'; + +export type AlertState = { + alertOpen: boolean, + alertType: ?string, + alertTitle: ?string, + alertMessage: ?string, + alertBtn: ?string, +}; + +const initialState: AlertState = { + alertOpen: false, + alertType: null, + alertTitle: null, + alertMessage: null, + alertBtn: null, +}; + +export default function alert( + state: AlertState = initialState, + action: Action, +): AlertState { + switch (action.type) { + case 'ALERT': { + const { + title, text, icon, confirmButtonText, + } = action; + + return { + ...state, + alertOpen: true, + alertTitle: title, + alertMessage: text, + alertType: icon, + alertBtn: confirmButtonText, + }; + } + + case 'CLOSE_ALERT': { + return { + ...state, + alertOpen: false, + }; + } + + default: + return state; + } +} diff --git a/src/reducers/index.js b/src/reducers/index.js index 0ad4c09..c62e651 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -8,6 +8,7 @@ import gui from './gui'; import modal from './modal'; import user from './user'; import ranks from './ranks'; +import alert from './alert'; import chat from './chat'; import contextMenu from './contextMenu'; import chatRead from './chatRead'; @@ -18,6 +19,8 @@ import type { CanvasState } from './canvas'; import type { GUIState } from './gui'; import type { ModalState } from './modal'; import type { UserState } from './user'; +import type { RanksState } from './ranks'; +import type { AlertState } from './alert'; import type { ChatState } from './chat'; import type { ContextMenuState } from './contextMenu'; import type { FetchingState } from './fetching'; @@ -28,6 +31,8 @@ export type State = { gui: GUIState, modal: ModalState, user: UserState, + ranks: RanksState, + alert: AlertState, chat: ChatState, contextMenu: ContextMenuState, chatRead: ChatReadState, @@ -41,6 +46,7 @@ const config = { 'user', 'ranks', 'canvas', + 'alert', 'modal', 'chat', 'contextMenu', @@ -55,6 +61,7 @@ export default persistCombineReducers(config, { modal, user, ranks, + alert, chat, contextMenu, chatRead, diff --git a/src/reducers/ranks.js b/src/reducers/ranks.js index fc87936..3c307d5 100644 --- a/src/reducers/ranks.js +++ b/src/reducers/ranks.js @@ -2,7 +2,7 @@ import type { Action } from '../actions/types'; -export type UserState = { +export type RanksState = { totalPixels: number, dailyTotalPixels: number, ranking: number, @@ -13,7 +13,7 @@ export type UserState = { totalDailyRanking: Object, }; -const initialState: UserState = { +const initialState: RanksState = { totalPixels: 0, dailyTotalPixels: 0, ranking: 1488, @@ -24,9 +24,9 @@ const initialState: UserState = { }; export default function ranks( - state: UserState = initialState, + state: RanksState = initialState, action: Action, -): UserState { +): RanksState { switch (action.type) { case 'PLACED_PIXELS': { let { totalPixels, dailyTotalPixels } = state; diff --git a/src/store/configureStore.js b/src/store/configureStore.js index 6303411..401c2de 100644 --- a/src/store/configureStore.js +++ b/src/store/configureStore.js @@ -5,7 +5,6 @@ import thunk from 'redux-thunk'; import { persistStore } from 'redux-persist'; import audio from './audio'; -import swal from './sweetAlert'; import protocolClientHook from './protocolClientHook'; import rendererHook from './rendererHook'; // import ads from './ads'; @@ -25,7 +24,6 @@ const store = createStore( thunk, promise, array, - swal, audio, notifications, title, diff --git a/src/store/sweetAlert.js b/src/store/sweetAlert.js deleted file mode 100644 index 72bbd8d..0000000 --- a/src/store/sweetAlert.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * @flow - */ - -import swal from 'sweetalert2'; - -export default () => (next) => (action) => { - switch (action.type) { - case 'ALERT': { - const { - title, - text, - icon, - confirmButtonText, - } = action; - swal.fire({ - title, - text, - icon, - confirmButtonText, - }); - break; - } - - default: - // nothing - } - - return next(action); -}; diff --git a/src/styles/default.css b/src/styles/default.css index 6cab70e..97318f5 100644 --- a/src/styles/default.css +++ b/src/styles/default.css @@ -322,7 +322,7 @@ tr:nth-child(even) { padding: 0; } -.Modal { +.Modal, .Alert { position: fixed; top: 50%; left: 50%; @@ -334,14 +334,21 @@ tr:nth-child(even) { border-radius: 4px; outline: currentcolor none medium; transform: translate(-50%, -50%); +} + +.Modal { height: 80%; - max-height: 900px; width: 70%; - transition: all 0.5s ease 0s; + max-height: 900px; +} + +.Alert { + padding: 15px; + text-align: center; } .modaltext, .modalcotext { - color: hsla(218, 5%, 47%, .6); + color: hsla(0, 0%, 0%, 0.6); font-size: 14px; font-weight: 500; line-height: normal; @@ -370,7 +377,7 @@ tr:nth-child(even) { .modaldesc { box-sizing: border-box; flex: 1 1 auto; - color: hsla(218, 5%, 47%, .6); + color: hsla(0, 0%, 0%, 0.6); font-size: 14px; line-height: 20px; font-weight: 500; @@ -397,7 +404,7 @@ tr:nth-child(even) { } .modalcvtext { - color: hsla(218, 5%, 47%, .6); + color: hsla(0, 0%, 0%, 0.6); font-size: 14px; font-weight: 500; padding: 0; @@ -446,6 +453,7 @@ tr:nth-child(even) { padding: 5%; } } + .Overlay { position: fixed; top: 0px; @@ -454,6 +462,8 @@ tr:nth-child(even) { bottom: 0px; background-color: rgba(255, 255, 255, 0.75); z-index: 4; + opacity: 0; + transition: opacity 200ms ease-in-out; } .chatbox div .chatarea { @@ -697,12 +707,7 @@ tr:nth-child(even) { visibility: hidden; } -.ReactModal__Overlay { - opacity: 0; - transition: opacity 200ms ease-in-out; -} - -.ReactModal__Overlay--after-open{ +.ReactModal__Overlay--after-open, .Overlay.show { opacity: 1; } From 7dd44811a6c7df59b1a9b0c7f55b7a3865d9e6a9 Mon Sep 17 00:00:00 2001 From: HF Date: Wed, 24 Feb 2021 03:41:39 +0100 Subject: [PATCH 07/32] add captcha alert, remove some old captcha stuff --- README.md | 34 +++++----- deployment/example-ecosystem-captchas.yml | 2 +- src/captchaserver.js | 4 +- src/components/Alert.jsx | 23 ++++--- src/components/Captcha.jsx | 74 ++++++++++++++++++++-- src/components/ChatBox.jsx | 4 +- src/components/HelpModal.jsx | 14 ----- src/components/SignUpForm.jsx | 4 ++ src/core/config.js | 8 +-- src/ssr-components/Html.jsx | 23 ------- src/ssr-components/Main.jsx | 3 +- src/ui/placePixel.js | 13 ++-- src/utils/captcha.js | 75 +++-------------------- 13 files changed, 127 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index b2b7c88..98040dd 100644 --- a/README.md +++ b/README.md @@ -79,24 +79,22 @@ 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_METHOD | 0: none, 1: reCaptcha, 2: hCaptcha | 2 | -| CAPTCHA_SECRET | re/hCaptcha secret key | "asdieewff" | -| CAPTCHA_SITEKEY | re/hCaptcha site key | "23ksdfssd" | -| CAPTCHA_TIME | time in minutes between captchas | 30 | -| 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_URL | URL where captcha is served | "http://localhost:8080" | +| CAPTCHA_TIME | time in minutes between captchas | 30 | +| 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 | Notes: diff --git a/deployment/example-ecosystem-captchas.yml b/deployment/example-ecosystem-captchas.yml index 107fd8b..f98120f 100644 --- a/deployment/example-ecosystem-captchas.yml +++ b/deployment/example-ecosystem-captchas.yml @@ -3,6 +3,6 @@ apps: name : 'captchas' node_args: --nouse-idle-notification --expose-gc env: - PORT: 80 + PORT: 8080 HOST: "localhost" REDIS_URL: 'redis://localhost:6379' diff --git a/src/captchaserver.js b/src/captchaserver.js index 5ba5a45..1ea20a1 100644 --- a/src/captchaserver.js +++ b/src/captchaserver.js @@ -8,7 +8,7 @@ import process from 'process'; import http from 'http'; import ppfunCaptcha from 'ppfun-captcha'; -const PORT = process.env.PORT || 80; +const PORT = process.env.PORT || 8080; const HOST = process.env.HOST || 'localhost'; const server = http.createServer((req, res) => { @@ -25,7 +25,7 @@ const server = http.createServer((req, res) => { const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; console.log(`Serving ${captcha.text} to ${ip}`); res.writeHead(200, { - 'Content-Type': 'text/html', + 'Content-Type': 'image/svg+xml', 'Cache-Control': 'no-cache', }); res.write(captcha.data); diff --git a/src/components/Alert.jsx b/src/components/Alert.jsx index c18ee30..242db59 100644 --- a/src/components/Alert.jsx +++ b/src/components/Alert.jsx @@ -3,9 +3,10 @@ * @flow */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { useSelector, useDispatch } from 'react-redux'; +import Captcha from './Captcha'; import { closeAlert } from '../actions'; const Alert = () => { @@ -20,9 +21,9 @@ const Alert = () => { } = useSelector((state) => state.alert); const dispatch = useDispatch(); - const close = () => { + const close = useCallback(() => { dispatch(closeAlert()); - }; + }, [dispatch]); const onTransitionEnd = () => { if (!alertOpen) setRender(false); @@ -54,12 +55,16 @@ const Alert = () => { {alertMessage}

- + {(alertType === 'captcha') + ? + : ( + + )}

diff --git a/src/components/Captcha.jsx b/src/components/Captcha.jsx index 78f97f8..73c2a5c 100644 --- a/src/components/Captcha.jsx +++ b/src/components/Captcha.jsx @@ -2,10 +2,74 @@ * @flow */ -import React from 'react'; +import React, { useState } from 'react'; import { t } from 'ttag'; -const Captcha = ({ -}) => ( - div /> -); +import { IoReloadCircleSharp } from 'react-icons/io5'; + +function getUrl() { + return `${window.ssv.captchaurl}/captcha.svg?${new Date().getTime()}`; +} + +const Captcha = ({ callback, cancel }) => { + const [captchaUrl, setCaptchaUrl] = useState(getUrl()); + const [text, setText] = useState(''); + const [error, setError] = useState(false); + + return ( +
+

+ {t`Type the characters from the following image:`} + + ({t`Tip: Not case-sensitive; I and l are the same`}) + +

+ setError(true)} + /> +

+ {t`Can't read? Reload:`}  + setCaptchaUrl(getUrl())} + > + + +

+ { + const txt = evt.target.value; + setText(txt); + if (callback) callback(txt); + }} + /> + {(!callback) && ( +
+ + +
+ )} +
+ ); +}; + +export default React.memo(Captcha); diff --git a/src/components/ChatBox.jsx b/src/components/ChatBox.jsx index 0fa3c12..df0b9ee 100644 --- a/src/components/ChatBox.jsx +++ b/src/components/ChatBox.jsx @@ -25,7 +25,7 @@ const ChatBox = () => { }, 10); }, [chatOpen]); - const onTransitionEnd =() => { + const onTransitionEnd = () => { if (!chatOpen) setRender(false); }; @@ -44,6 +44,6 @@ const ChatBox = () => {
) ); -} +}; export default React.memo(ChatBox); diff --git a/src/components/HelpModal.jsx b/src/components/HelpModal.jsx index 2729be3..4af09da 100644 --- a/src/components/HelpModal.jsx +++ b/src/components/HelpModal.jsx @@ -90,20 +90,6 @@ can be downloaded from mega.nz here: `}crazygames.com

- { (typeof window.hcaptcha === 'undefined') - ? ( -

- - {jt`This site is protected by reCAPTCHA and the Google ${reCaptchaPP} and ${reCaptchaTOS} apply.`} - -

- ) : ( -

- - {jt`This site is protected by hCAPTCHA and its ${hCaptchaPP} and ${hCaptchaTOS} apply.`} - -

- )}

); }; diff --git a/src/components/SignUpForm.jsx b/src/components/SignUpForm.jsx index 153ebdf..74a1650 100644 --- a/src/components/SignUpForm.jsx +++ b/src/components/SignUpForm.jsx @@ -121,6 +121,7 @@ class SignUpForm extends React.Component { this.setState({ name: evt.target.value })} type="text" placeholder={t`Name`} @@ -128,6 +129,7 @@ class SignUpForm extends React.Component { this.setState({ email: evt.target.value })} type="text" placeholder={t`Email`} @@ -135,6 +137,7 @@ class SignUpForm extends React.Component { this.setState({ password: evt.target.value })} type="password" placeholder={t`Password`} @@ -142,6 +145,7 @@ class SignUpForm extends React.Component { this.setState({ confirmPassword: evt.target.value, })} diff --git a/src/core/config.js b/src/core/config.js index b807ac6..521ab2d 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -20,6 +20,8 @@ export const TILE_FOLDER = path.join(__dirname, `./${TILE_FOLDER_REL}`); export const ASSET_SERVER = process.env.ASSET_SERVER || '.'; +export const CAPTCHA_URL = process.env.CAPTCHA_URL || 'http://localhost:8080'; + export const USE_XREALIP = process.env.USE_XREALIP || false; export const BACKUP_URL = process.env.BACKUP_URL || null; @@ -86,12 +88,6 @@ export const auth = { }, }; -// o: none -// 1: reCaptcha -// 2: hCaptcha -export const CAPTCHA_METHOD = Number(process.env.CAPTCHA_METHOD || 0); -export const CAPTCHA_SECRET = process.env.CAPTCHA_SECRET || false; -export const CAPTCHA_SITEKEY = process.env.CAPTCHA_SITEKEY || false; // time on which to display captcha in minutes export const CAPTCHA_TIME = parseInt(process.env.CAPTCHA_TIME, 10) || 30; diff --git a/src/ssr-components/Html.jsx b/src/ssr-components/Html.jsx index 8d7dbe2..02e3b7e 100644 --- a/src/ssr-components/Html.jsx +++ b/src/ssr-components/Html.jsx @@ -11,7 +11,6 @@ /* eslint-disable max-len */ import React from 'react'; -import { CAPTCHA_METHOD, CAPTCHA_SITEKEY } from '../core/config'; const Html = ({ title, @@ -48,10 +47,6 @@ const Html = ({ dangerouslySetInnerHTML={{ __html: style.cssText }} /> ))} - {(CAPTCHA_METHOD === 1) && CAPTCHA_SITEKEY && useCaptcha - &&