adding fake cloudflare error page for private_society spoofing

This commit is contained in:
2026-05-13 10:10:53 +02:00
parent d720532661
commit 365c0a4b29
6 changed files with 429 additions and 85 deletions

View File

@@ -93,6 +93,7 @@
"open_registration": true, "open_registration": true,
"open_registration_web_toggle": false, "open_registration_web_toggle": false,
"private_society": false, "private_society": false,
"private_society_gate": "cloudflare",
"paths": { "paths": {
"images": "/b", "images": "/b",

158
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@ruffle-rs/ruffle": "0.2.0-nightly.2026.4.21", "@ruffle-rs/ruffle": "0.2.0-nightly.2026.4.21",
"cloudflare-error-page": "^0.1.0",
"cuffeo": "git+https://git.lat/kibi/scuffeo.git", "cuffeo": "git+https://git.lat/kibi/scuffeo.git",
"flumm-fetch": "^1.0.1", "flumm-fetch": "^1.0.1",
"flummpress": "^2.0.5", "flummpress": "^2.0.5",
@@ -55,12 +56,33 @@
"integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==", "integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==",
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
"node_modules/base-x": { "node_modules/base-x": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz", "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz",
"integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==", "integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/brace-expansion": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/bs58": { "node_modules/bs58": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz",
@@ -70,6 +92,18 @@
"base-x": "^5.0.0" "base-x": "^5.0.0"
} }
}, },
"node_modules/cloudflare-error-page": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/cloudflare-error-page/-/cloudflare-error-page-0.1.0.tgz",
"integrity": "sha512-Eogslg+b+hcGjzjdD/DeDD5/Yet0U6eck49FVrkrbTBy9rLMWPMpWd4otIG+vDSmqc9GVxpuBYV7PjNv3wAm4A==",
"license": "MIT",
"dependencies": {
"ejs": "^3.1.10"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/content-type": { "node_modules/content-type": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
@@ -93,6 +127,21 @@
"flumm-fetch": "^1.0.1" "flumm-fetch": "^1.0.1"
} }
}, },
"node_modules/ejs": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"license": "Apache-2.0",
"dependencies": {
"jake": "^10.8.5"
},
"bin": {
"ejs": "bin/cli.js"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/events": { "node_modules/events": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
@@ -102,6 +151,15 @@
"node": ">=0.8.x" "node": ">=0.8.x"
} }
}, },
"node_modules/filelist": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
"integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
"license": "Apache-2.0",
"dependencies": {
"minimatch": "^5.0.1"
}
},
"node_modules/flumm-fetch": { "node_modules/flumm-fetch": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz", "resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
@@ -142,6 +200,23 @@
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/jake": {
"version": "10.9.4",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
"license": "Apache-2.0",
"dependencies": {
"async": "^3.2.6",
"filelist": "^1.0.4",
"picocolors": "^1.1.1"
},
"bin": {
"jake": "bin/cli.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/jszip": { "node_modules/jszip": {
"version": "3.10.1", "version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -238,6 +313,18 @@
"events": "^3.2.0" "events": "^3.2.0"
} }
}, },
"node_modules/minimatch": {
"version": "5.1.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/oidc-client-ts": { "node_modules/oidc-client-ts": {
"version": "3.4.1", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.4.1.tgz", "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.4.1.tgz",
@@ -271,6 +358,12 @@
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)" "license": "(MIT AND Zlib)"
}, },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/postgres": { "node_modules/postgres": {
"version": "3.3.4", "version": "3.3.4",
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz", "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",
@@ -383,11 +476,29 @@
"resolved": "https://registry.npmjs.org/another-json/-/another-json-0.2.0.tgz", "resolved": "https://registry.npmjs.org/another-json/-/another-json-0.2.0.tgz",
"integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==" "integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg=="
}, },
"async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"base-x": { "base-x": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz", "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz",
"integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==" "integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="
}, },
"brace-expansion": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
"requires": {
"balanced-match": "^1.0.0"
}
},
"bs58": { "bs58": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz",
@@ -396,6 +507,14 @@
"base-x": "^5.0.0" "base-x": "^5.0.0"
} }
}, },
"cloudflare-error-page": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/cloudflare-error-page/-/cloudflare-error-page-0.1.0.tgz",
"integrity": "sha512-Eogslg+b+hcGjzjdD/DeDD5/Yet0U6eck49FVrkrbTBy9rLMWPMpWd4otIG+vDSmqc9GVxpuBYV7PjNv3wAm4A==",
"requires": {
"ejs": "^3.1.10"
}
},
"content-type": { "content-type": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
@@ -413,11 +532,27 @@
"flumm-fetch": "^1.0.1" "flumm-fetch": "^1.0.1"
} }
}, },
"ejs": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"requires": {
"jake": "^10.8.5"
}
},
"events": { "events": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
}, },
"filelist": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
"integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
"requires": {
"minimatch": "^5.0.1"
}
},
"flumm-fetch": { "flumm-fetch": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz", "resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
@@ -448,6 +583,16 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
}, },
"jake": {
"version": "10.9.4",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
"requires": {
"async": "^3.2.6",
"filelist": "^1.0.4",
"picocolors": "^1.1.1"
}
},
"jszip": { "jszip": {
"version": "3.10.1", "version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -517,6 +662,14 @@
"events": "^3.2.0" "events": "^3.2.0"
} }
}, },
"minimatch": {
"version": "5.1.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"requires": {
"brace-expansion": "^2.0.1"
}
},
"oidc-client-ts": { "oidc-client-ts": {
"version": "3.4.1", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.4.1.tgz", "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.4.1.tgz",
@@ -538,6 +691,11 @@
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
}, },
"picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"postgres": { "postgres": {
"version": "3.3.4", "version": "3.3.4",
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz", "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",

View File

@@ -22,12 +22,13 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@ruffle-rs/ruffle": "0.2.0-nightly.2026.4.21", "@ruffle-rs/ruffle": "0.2.0-nightly.2026.4.21",
"cloudflare-error-page": "^0.1.0",
"cuffeo": "git+https://git.lat/kibi/scuffeo.git", "cuffeo": "git+https://git.lat/kibi/scuffeo.git",
"flumm-fetch": "^1.0.1", "flumm-fetch": "^1.0.1",
"flummpress": "^2.0.5", "flummpress": "^2.0.5",
"jszip": "^3.10.1",
"marked": "^18.0.2", "marked": "^18.0.2",
"matrix-js-sdk": "^40.3.0-rc.0", "matrix-js-sdk": "^40.3.0-rc.0",
"postgres": "^3.3.4", "postgres": "^3.3.4"
"jszip": "^3.10.1"
} }
} }

View File

@@ -115,7 +115,7 @@ export default (router, tpl) => {
from "user" from "user"
where "login" = ${username.toLowerCase()} where "login" = ${username.toLowerCase()}
or "user" = ${username} or "user" = ${username}
or ("email" is not null and "email" = ${email}) ${email ? db`or ("email" is not null and "email" = ${email})` : db``}
`; `;
if (existing.length > 0) { if (existing.length > 0) {

View File

@@ -21,7 +21,52 @@ import { updateHallsCache, getHalls } from "./inc/halls_cache.mjs";
import { createI18n } from "./inc/i18n.mjs"; import { createI18n } from "./inc/i18n.mjs";
import security from "./inc/security.mjs"; import security from "./inc/security.mjs";
const nginx502 = `<html> import { createRequire } from 'module';
const _require = createRequire(import.meta.url);
// ─── Private Society Gate ────────────────────────────────────────────────────
// Powered by the cloudflare-error-page package.
// Each request gets a fresh page with a unique Ray ID + current UTC timestamp.
//
// Customise the visible text and status icons here:
const gateOptions = {
title: 'Bad Gateway',
error_code: '502',
what_happened: "There is an internal server error on Cloudflare&#39;s network.",
// what_can_i_do is injected dynamically below (Sign in button)
error_source: 'host',
browser_status: {
status: 'ok',
location: 'You',
name: 'Browser',
status_text: 'Working',
},
cloudflare_status: {
status: 'ok',
location: 'Frankfurt',
name: 'Cloudflare',
status_text: 'Working',
},
host_status: {
status: 'error',
location: 'Website',
name: 'Host',
status_text: 'Error',
},
more_information: {
hidden: false,
text: 'cloudflare.com',
link: '',
for: 'more information',
},
perf_sec_by: { text: '', link: '' },
};
// Fallback when the package isn't installed
const nginx502Fallback = `<html>
<head><title>502 Bad Gateway</title></head> <head><title>502 Bad Gateway</title></head>
<body bgcolor="white"> <body bgcolor="white">
<center><h1>502 Bad Gateway</h1></center> <center><h1>502 Bad Gateway</h1></center>
@@ -29,6 +74,200 @@ const nginx502 = `<html>
</body> </body>
</html>`; </html>`;
// Login + Register modal injected before </body>
const gateLoginInjection = `
<div id="hot-corner" style="position:fixed;bottom:0;left:0;width:20px;height:20px;z-index:9999;"></div>
<div id="gate-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:10000;align-items:center;justify-content:center;">
<div style="background:#f0f0f0;border:1px solid #999;color:#404040;border-radius:0;box-shadow:4px 4px 8px rgba(0,0,0,0.25);font-family:system-ui,-apple-system,sans-serif;padding:28px 32px;min-width:340px;max-width:94vw;width:360px;position:relative;">
<button id="gate-modal-close" style="position:absolute;top:10px;right:14px;background:none;border:none;font-size:22px;color:#666;cursor:pointer;line-height:1;">&times;</button>
<!-- Login View -->
<div id="gate-login-view">
<form id="gate-login-form" novalidate style="display:flex;flex-direction:column;gap:10px;">
<h2 style="text-align:center;margin:0 0 14px;font-size:1.2em;font-weight:600;color:#222;">Sign in</h2>
<div id="gate-login-error" style="display:none;background:#fde8e8;border:1px solid #e0a0a0;color:#bd2426;padding:7px 10px;font-size:13px;text-align:center;"></div>
<input type="text" name="username" placeholder="Username or email" autocomplete="off" required
style="background:white;color:black;border:1px solid #bbb;padding:7px 10px;width:100%;box-sizing:border-box;font-size:14px;font-family:inherit;" />
<input type="password" name="password" placeholder="Password" autocomplete="off" required
style="background:white;color:black;border:1px solid #bbb;padding:7px 10px;width:100%;box-sizing:border-box;font-size:14px;font-family:inherit;" />
<label style="font-size:12px;color:#555;display:flex;align-items:center;gap:6px;"><input type="checkbox" name="kmsi" style="margin:0;"> Stay signed in</label>
<button type="submit" id="gate-login-btn" style="background:#0051c3;color:white;border:none;padding:9px;font-weight:600;font-size:14px;cursor:pointer;font-family:inherit;"
onmouseover="this.style.background='#003681'" onmouseout="if(!this.disabled)this.style.background='#0051c3'">Sign in</button>
<p style="text-align:center;font-size:0.85em;margin:6px 0 0;color:#555;">
No account? <a href="#" id="gate-to-register" style="color:#0051c3;text-decoration:underline;">Register</a>
</p>
</form>
</div>
<!-- Register View -->
<div id="gate-register-view" style="display:none;">
<form id="gate-register-form" novalidate style="display:flex;flex-direction:column;gap:10px;">
<h2 style="text-align:center;margin:0 0 14px;font-size:1.2em;font-weight:600;color:#222;">Create account</h2>
<div id="gate-register-error" style="display:none;background:#fde8e8;border:1px solid #e0a0a0;color:#bd2426;padding:7px 10px;font-size:13px;text-align:center;"></div>
<div id="gate-register-ok" style="display:none;background:#e8fde8;border:1px solid #a0e0a0;color:#2a7a2a;padding:7px 10px;font-size:13px;text-align:center;"></div>
<input type="text" name="username" placeholder="Username" autocomplete="off" required
style="background:white;color:black;border:1px solid #bbb;padding:7px 10px;width:100%;box-sizing:border-box;font-size:14px;font-family:inherit;" />
<input type="password" name="password" placeholder="Password (min. 20 characters)" autocomplete="off" required minlength="20"
style="background:white;color:black;border:1px solid #bbb;padding:7px 10px;width:100%;box-sizing:border-box;font-size:14px;font-family:inherit;" />
<input type="password" name="password_confirm" placeholder="Confirm password" autocomplete="off" required minlength="20"
style="background:white;color:black;border:1px solid #bbb;padding:7px 10px;width:100%;box-sizing:border-box;font-size:14px;font-family:inherit;" />
<input type="text" name="token" placeholder="Invite token" autocomplete="off"
style="background:white;color:black;border:1px solid #bbb;padding:7px 10px;width:100%;box-sizing:border-box;font-size:14px;font-family:inherit;" />
<input type="text" name="email_confirm_field" style="display:none !important;" tabindex="-1" autocomplete="off" />
<button type="submit" id="gate-register-btn" style="background:#0051c3;color:white;border:none;padding:9px;font-weight:600;font-size:14px;cursor:pointer;font-family:inherit;"
onmouseover="this.style.background='#003681'" onmouseout="if(!this.disabled)this.style.background='#0051c3'">Create account</button>
<p style="text-align:center;font-size:0.85em;margin:6px 0 0;color:#555;">
<a href="#" id="gate-to-login" style="color:#0051c3;text-decoration:underline;">Back to sign in</a>
</p>
</form>
</div>
</div>
</div>
<script>
function openLoginGate(view) {
var m = document.getElementById('gate-modal');
if (!m) return;
m.style.display = 'flex';
gateShowView(view || 'login');
}
function gateShowView(view) {
document.getElementById('gate-login-view').style.display = view === 'login' ? '' : 'none';
document.getElementById('gate-register-view').style.display = view === 'register' ? '' : 'none';
}
function gateSetError(id, msg) {
var el = document.getElementById(id);
if (!el) return;
el.textContent = msg;
el.style.display = msg ? '' : 'none';
}
function gateSetBtn(id, loading) {
var btn = document.getElementById(id);
if (!btn) return;
btn.disabled = loading;
btn.style.opacity = loading ? '0.65' : '1';
btn.style.cursor = loading ? 'default' : 'pointer';
}
document.addEventListener('DOMContentLoaded', function() {
var modal = document.getElementById('gate-modal');
var close = document.getElementById('gate-modal-close');
if (close) close.onclick = function() { modal.style.display = 'none'; };
if (modal) modal.addEventListener('click', function(e) { if (e.target === this) this.style.display = 'none'; });
document.getElementById('gate-to-register').onclick = function(e) { e.preventDefault(); gateShowView('register'); };
document.getElementById('gate-to-login').onclick = function(e) { e.preventDefault(); gateShowView('login'); };
var hc = document.getElementById('hot-corner');
if (hc) hc.onclick = function() { openLoginGate('login'); };
// ── Login form ──────────────────────────────────────────────────────────
document.getElementById('gate-login-form').onsubmit = function(e) {
e.preventDefault();
gateSetError('gate-login-error', '');
gateSetBtn('gate-login-btn', true);
var fd = new FormData(this);
var body = new URLSearchParams(fd).toString();
fetch('/login', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },
body: body
})
.then(function(r) { return r.json(); })
.then(function(d) {
if (d.success === false) {
gateSetError('gate-login-error', d.msg || 'Login failed.');
gateSetBtn('gate-login-btn', false);
} else {
// success — server set the cookie, reload to enter the site
window.location.reload();
}
})
.catch(function() {
// On redirect (301) fetch follows it — if the redirect lands on '/' we reload
window.location.reload();
});
};
// ── Register form ───────────────────────────────────────────────────────
document.getElementById('gate-register-form').onsubmit = function(e) {
e.preventDefault();
gateSetError('gate-register-error', '');
gateSetError('gate-register-ok', '');
gateSetBtn('gate-register-btn', true);
var fd = new FormData(this);
var body = new URLSearchParams(fd).toString();
fetch('/register', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },
body: body
})
.then(function(r) { return r.json(); })
.then(function(d) {
gateSetBtn('gate-register-btn', false);
if (d.success === false) {
gateSetError('gate-register-error', d.msg || 'Registration failed.');
} else {
document.getElementById('gate-register-ok').textContent = d.msg || 'Account created! You can now sign in.';
document.getElementById('gate-register-ok').style.display = '';
document.getElementById('gate-register-form').reset();
setTimeout(function() { gateShowView('login'); }, 2000);
}
})
.catch(function() {
gateSetBtn('gate-register-btn', false);
gateSetError('gate-register-error', 'An error occurred. Please try again.');
});
};
});
var _sb = '';
document.addEventListener('keydown', function(e) {
_sb += e.key.toLowerCase();
if (_sb.endsWith('premiumhumor')) { openLoginGate('login'); _sb = ''; }
if (_sb.length > 12) _sb = _sb.slice(-12);
});
</script>
`;
// Text injected into the "What can I do?" section
const gateSignInButton = `Please try again in a few minutes.`;
let _cfRender = null;
try {
_cfRender = _require('cloudflare-error-page').render;
console.log('[BOOT] cloudflare-error-page loaded — gate pages generated dynamically per request');
} catch (e) {
console.warn('[BOOT] cloudflare-error-page not installed, falling back to plain nginx 502:', e.message);
}
// Called on every gated request — produces a fresh page with unique Ray ID + timestamp
function buildGatePage() {
if (!_cfRender) return nginx502Fallback;
let html = _cfRender({ ...gateOptions, what_can_i_do: gateSignInButton });
// Make the second 'a' in "Bad Gateway" a secret click trigger
html = html.replace(
'<span class="inline-block">Bad Gateway</span>',
'<span class="inline-block">Bad Gatew<span id="secret-letter" style="cursor:text;" onclick="openLoginGate(\'login\')">a</span>y</span>'
);
html = html.replace('</body>', gateLoginInjection + '\n</body>');
return html;
}
// nginx502 === null signals "dynamic cloudflare mode" to the middleware below
const nginx502 = (cfg.websrv.private_society && cfg.websrv.private_society_gate === 'cloudflare')
? null
: nginx502Fallback;
if (nginx502 === null) console.log('[BOOT] Private society gate: Cloudflare dynamic mode');
// ─────────────────────────────────────────────────────────────────────────────
const origLog = console.log; const origLog = console.log;
console.log = function(...args) { console.log = function(...args) {
@@ -229,7 +468,7 @@ process.on('uncaughtException', err => {
return; return;
if (req.url.pathname.match(/^\/(b|t|ca|a|memes)\//) || req.url.pathname.startsWith('/s/emojis/')) { if (req.url.pathname.match(/^\/(b|t|ca|a|memes)\//) || req.url.pathname.startsWith('/s/emojis/')) {
if (cfg.websrv.private_society && !req.cookies?.session) { if (cfg.websrv.private_society && !req.cookies?.session) {
res.writeHead(502, { 'Content-Type': 'text/html' }).end(nginx502); res.writeHead(502, { 'Content-Type': 'text/html' }).end(nginx502 ?? buildGatePage());
req.url.pathname = '/private_society_media_bypass'; req.url.pathname = '/private_society_media_bypass';
return; return;
} }
@@ -419,7 +658,13 @@ process.on('uncaughtException', err => {
} }
// For page requests, return 502 Bad Gateway for all paths except homepage (which shows the gate) // For page requests, return 502 Bad Gateway for all paths except homepage (which shows the gate)
if (req.url.pathname !== '/') { if (req.url.pathname !== '/') {
res.writeHead(502, { 'Content-Type': 'text/html' }).end(nginx502); res.writeHead(502, { 'Content-Type': 'text/html' }).end(nginx502 ?? buildGatePage());
req.url.pathname = '/private_society_bypass';
return;
}
// Homepage: in cloudflare dynamic mode serve gate directly; otherwise fall through to template
if (nginx502 === null) {
res.writeHead(502, { 'Content-Type': 'text/html' }).end(buildGatePage());
req.url.pathname = '/private_society_bypass'; req.url.pathname = '/private_society_bypass';
return; return;
} }
@@ -783,6 +1028,7 @@ process.on('uncaughtException', err => {
development: cfg.main.development || false, development: cfg.main.development || false,
show_mime_picker: cfg.websrv.show_mime_picker !== false, show_mime_picker: cfg.websrv.show_mime_picker !== false,
private_society: cfg.websrv.private_society || false, private_society: cfg.websrv.private_society || false,
private_society_gate: cfg.websrv.private_society_gate || '',
show_content_warning: cfg.websrv.show_content_warning !== false, show_content_warning: cfg.websrv.show_content_warning !== false,
web_url_upload: !!cfg.websrv.web_url_upload, web_url_upload: !!cfg.websrv.web_url_upload,
enable_youtube_upload: cfg.websrv.enable_youtube_upload !== false, enable_youtube_upload: cfg.websrv.enable_youtube_upload !== false,

View File

@@ -12,73 +12,22 @@
<style> <style>
#nginx-502-gate { #nginx-502-gate {
background: white !important; background: white !important; color: black !important;
color: black !important; font-family: serif !important; font-size: 16px !important; margin: 1px !important;
font-family: serif !important;
font-size: 16px !important;
margin: 1px !important;
} }
#nginx-502-gate center { display: block !important; text-align: center !important; margin: 0 !important; padding: 0 !important;} #nginx-502-gate center { display: block !important; text-align: center !important; margin: 0 !important; padding: 0 !important; }
#nginx-502-gate h1 { font-family: serif !important; font-size: 2em !important; font-weight: bold !important; margin: .67em 0 !important; display: block !important; color: black !important; cursor: text !important;} #nginx-502-gate h1 { font-family: serif !important; font-size: 2em !important; font-weight: bold !important; margin: .67em 0 !important; display: block !important; color: black !important; cursor: text !important; }
#nginx-502-gate hr { margin: 15px 0 !important; display: block !important; border: 0 !important; border-top: 2px solid gray !important; } #nginx-502-gate hr { margin: 15px 0 !important; display: block !important; border: 0 !important; border-top: 2px solid gray !important; }
#secret-letter { #secret-letter { cursor: text; }
cursor: text; #login-modal, #register-modal { background: rgba(0,0,0,0.1) !important; z-index: 10000 !important; }
} #login-modal .login-modal-content, #register-modal .login-modal-content { background: #f0f0f0 !important; border: 1px solid #777 !important; color: black !important; border-radius: 0 !important; box-shadow: 4px 4px 0 rgba(0,0,0,0.2) !important; font-family: sans-serif !important; }
#secret-letter { cursor: text; } .login-modal-content h2 { color: black !important; margin-bottom: 15px !important; font-size: 1.3em !important; }
.login-form input[type="text"], .login-form input[type="password"], .login-form input[type="email"] { background: white !important; color: black !important; border: 1px solid #999 !important; border-radius: 0 !important; padding: 6px !important; width: 100% !important; box-sizing: border-box !important; margin-bottom: 10px !important; }
#login-modal, #register-modal { .login-form button[type="submit"] { background: #e1e1e1 !important; color: black !important; border: 1px solid #777 !important; border-radius: 0 !important; padding: 8px !important; font-weight: bold !important; cursor: pointer !important; opacity: 1 !important; }
background: rgba(0,0,0,0.1) !important; .login-form button[type="submit"]:hover { background: #d1d1d1 !important; }
z-index: 10000 !important; .login-form label, .login-form p, .login-form a { color: #444 !important; font-size: 0.9em !important; }
} .login-form a { text-decoration: underline !important; }
#login-modal .login-modal-content, #register-modal .login-modal-content { #login-modal-close, #register-modal-close { color: #444 !important; font-size: 24px !important; }
background: #f0f0f0 !important;
border: 1px solid #777 !important;
color: black !important;
border-radius: 0 !important;
box-shadow: 4px 4px 0 rgba(0,0,0,0.2) !important;
font-family: sans-serif !important;
}
.login-modal-content h2 {
color: black !important;
margin-bottom: 15px !important;
font-size: 1.3em !important;
}
.login-form input[type="text"],
.login-form input[type="password"],
.login-form input[type="email"] {
background: white !important;
color: black !important;
border: 1px solid #999 !important;
border-radius: 0 !important;
padding: 6px !important;
width: 100% !important;
box-sizing: border-box !important;
margin-bottom: 10px !important;
}
.login-form button[type="submit"] {
background: #e1e1e1 !important;
color: black !important;
border: 1px solid #777 !important;
border-radius: 0 !important;
padding: 8px !important;
font-weight: bold !important;
cursor: pointer !important;
opacity: 1 !important;
}
.login-form button[type="submit"]:hover {
background: #d1d1d1 !important;
}
.login-form label, .login-form p, .login-form a {
color: #444 !important;
font-size: 0.9em !important;
}
.login-form a {
text-decoration: underline !important;
}
#login-modal-close, #register-modal-close {
color: #444 !important;
font-size: 24px !important;
}
</style> </style>
<div id="gate-container" style="display:none;"> <div id="gate-container" style="display:none;">
@@ -89,7 +38,6 @@
function openLoginGate() { function openLoginGate() {
const container = document.getElementById('gate-container'); const container = document.getElementById('gate-container');
container.style.display = 'block'; container.style.display = 'block';
if (!document.getElementById('injected-gate-styles')) { if (!document.getElementById('injected-gate-styles')) {
const link = document.createElement('link'); const link = document.createElement('link');
link.id = 'injected-gate-styles'; link.id = 'injected-gate-styles';
@@ -97,32 +45,22 @@
link.href = '/s/css/f0ckm.css?v={{ ts }}'; link.href = '/s/css/f0ckm.css?v={{ ts }}';
document.head.appendChild(link); document.head.appendChild(link);
} }
const modal = document.getElementById('login-modal'); const modal = document.getElementById('login-modal');
if (modal) { if (modal) {
modal.style.display = 'flex'; modal.style.display = 'flex';
} else { } else {
const checkModal = setInterval(() => { const checkModal = setInterval(() => {
const m = document.getElementById('login-modal'); const m = document.getElementById('login-modal');
if (m) { if (m) { m.style.display = 'flex'; clearInterval(checkModal); }
m.style.display = 'flex';
clearInterval(checkModal);
}
}, 50); }, 50);
} }
} }
document.getElementById('secret-letter').onclick = openLoginGate; document.getElementById('secret-letter').onclick = openLoginGate;
document.getElementById('hot-corner').onclick = openLoginGate; document.getElementById('hot-corner').onclick = openLoginGate;
let secretBuffer = ''; let secretBuffer = '';
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
secretBuffer += e.key.toLowerCase(); secretBuffer += e.key.toLowerCase();
if (secretBuffer.endsWith('premiumhumor')) { if (secretBuffer.endsWith('premiumhumor')) { openLoginGate(); secretBuffer = ''; }
openLoginGate();
secretBuffer = '';
}
if (secretBuffer.length > 10) secretBuffer = secretBuffer.substring(1); if (secretBuffer.length > 10) secretBuffer = secretBuffer.substring(1);
}); });
</script> </script>