From 3de09009d6951ff7751c53883dd6fa3faa49034c Mon Sep 17 00:00:00 2001
From: anima
Date: Mon, 2 Nov 2026 02:16:44 +0100
Subject: [PATCH] fix mail verify
---
Dockerfile | 10 ++-
README.md | 32 +++++++--
docker/patch-auto-verify-email.js | 106 ++++++++++++++++++++++++++++++
files/config/options.js | 6 +-
verify_user.sh | 14 ++++
5 files changed, 159 insertions(+), 9 deletions(-)
create mode 100644 docker/patch-auto-verify-email.js
create mode 100644 verify_user.sh
diff --git a/Dockerfile b/Dockerfile
index 773e42f..dc5756e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -59,13 +59,21 @@ COPY docker/patch-attribution.js /tmp/patch-attribution.js
RUN node /tmp/patch-attribution.js /app/htmls/contents/account_help.html
# Local patch: custom game values and elements (docker/patch-custom-game.js).
-# Hooks the loader into main.js and the game server, makes the Lone Wolf rates
+# Hooks the loader into main.js and the game server, makes the e.g. Lone Wolf rates
# data-driven, and merges phrase overrides into the language catalogs. The
# loader itself is copied to /app/docker/ in the runtime stage; the custom file
# is mounted at runtime from ./files/custom.
COPY docker/patch-custom-game.js /tmp/patch-custom-game.js
RUN node /tmp/patch-custom-game.js /app
+# Local patch: optional email auto-verification (docker/patch-auto-verify-email.js).
+# A self-hosted server without SES cannot deliver verification links, so every
+# new account would stay "Not Verified". The patch marks accounts verified at
+# signup and login when options.auto_verify_email is enabled; without the
+# option the upstream behaviour is unchanged.
+COPY docker/patch-auto-verify-email.js /tmp/patch-auto-verify-email.js
+RUN node /tmp/patch-auto-verify-email.js /app
+
FROM ${NODE_IMAGE} AS runtime
diff --git a/README.md b/README.md
index d87d7fb..fd9d954 100644
--- a/README.md
+++ b/README.md
@@ -139,6 +139,12 @@ passt.
Backend und Game-Server ein und macht die Lone-Wolf-Raten datengetrieben (siehe
„Spieldaten anpassen“). Fehlt ein Anker, bricht der Build ebenfalls ab.
+`docker/patch-auto-verify-email.js` markiert bei aktivem
+`options.auto_verify_email` neue Konten beim Signup und bestehende beim nächsten
+Login als verifiziert und überspringt die Verifikationsmail. Ohne den Schalter
+bleibt das Upstream-Verhalten unverändert. Auch dieser Patch bricht den Build
+ab, wenn ein Anker in `api.js` fehlt.
+
## Spieldaten anpassen (`files/custom/game.js`)
Werte bestehender Elemente und neue Elemente lassen sich ohne Quelltextänderung
@@ -183,12 +189,13 @@ Custom-Datei zurückgesetzt werden; dann den Game-Server neu starten.
## Gut zu wissen
-- **E-Mail-Verifizierung:** AWS SES ist nicht konfiguriert. Beim Registrieren
- erscheint im Backend-Log `send_email error InvalidClientTokenId`. Das ist
- erwartet – die Registrierung funktioniert trotzdem, es wird nur keine Mail
- verschickt. Bis zur Verifizierung hängt das Konto an „Not Verified“
- (Gold/Glück −25 %). Der Verifizierungslink funktioniert auch ohne Mailversand;
- er lässt sich in MongoDB nachsehen:
+- **E-Mail-Verifizierung:** AWS SES ist nicht konfiguriert. Deshalb setzt
+ `options.auto_verify_email: true` neue Konten beim Signup und bestehende beim
+ nächsten Login auf „verifiziert“; die Verifikationsmail wird übersprungen. Der
+ „Not Verified“-Debuff (Gold/Glück −25 %) tritt damit nicht mehr auf. Auf
+ `false` gestellt, gilt wieder der Upstream-Ablauf: Im Backend-Log erscheint
+ `send_email error InvalidClientTokenId`, und der Verifizierungslink muss
+ manuell geöffnet werden:
```sh
docker compose exec al-mongo mongosh --quiet adventureland --eval \
@@ -197,7 +204,18 @@ Custom-Datei zurückgesetzt werden; dann den Game-Server neu starten.
```
Link im Browser öffnen, danach neu einloggen. Alternativ `info.verified` direkt
- auf `true` setzen (siehe Admin-Befehl oben).
+ auf `true` setzen (siehe Admin-Befehl oben). Die `/ev`-Route bleibt in beiden
+ Fällen funktionsfähig.
+
+- **Tutorial-Belohnung (`c0`):** Jedes Konto mit `auth_id` (Steam/MAS) kann nach
+ abgeschlossenem Tutorial einmal `c0` abholen. Die Belohnung ist ein zufälliger
+ kosmetischer Look aus dem `cosmo0`-Pool (über 100 Varianten). Der Game-Server
+ prüft dafür `player.verified` **und** `player.auth_id`
+ (`node/server.js:5272`). Bei reinen Web-Accounts ohne Steam/MAS bleibt
+ `auth_id` leer, deshalb ist die Belohnung trotz Auto-Verifizierung weiterhin
+ gesperrt. Die alten Klassen-Belohnungen (`creward`) sind in
+ `design/classes.js` auskommentiert und werden vom Client nicht angefragt;
+ Hardcore-Rewards gelten nur auf dem Hardcore-Server.
- **„Authorization Failure“ (Gold/Glück −85 %, XP −20 %):** Das ist die
DRM-Prüfung des Game-Servers. Sie verlangt für jedes Konto ab 2019 eine
diff --git a/docker/patch-auto-verify-email.js b/docker/patch-auto-verify-email.js
new file mode 100644
index 0000000..4d7b2fb
--- /dev/null
+++ b/docker/patch-auto-verify-email.js
@@ -0,0 +1,106 @@
+#!/usr/bin/env node
+// Local patch: optional email auto-verification for self-hosted servers.
+//
+// Adventure Land normally emails a verification link after signup. A
+// self-hosted server without SES/SMTP cannot deliver it, so every new account
+// stays "Not Verified" (luck/gold -25%) until an admin verifies it manually.
+//
+// With options.auto_verify_email enabled, this patch:
+// - marks new accounts verified before they are saved,
+// - verifies existing accounts on their next login,
+// - keeps accounts verified when they change their email address,
+// - skips the verification emails that cannot be delivered.
+//
+// Upstream behaviour is unchanged when the option is absent or false. Every
+// edit is idempotent and fails loudly if the anchor is gone.
+
+const fs = require("node:fs");
+const path = require("node:path");
+
+const app = path.resolve(process.argv[2] || "/app");
+const file = path.join(app, "api.js");
+let source = fs.readFileSync(file, "utf8");
+
+// `options` is always present in production, but some upstream tests load
+// api.js without it. Keep the patched code loadable there too.
+const autoVerify = 'typeof options !== "undefined" && options.auto_verify_email';
+
+function fail(message) {
+ console.error("patch-auto-verify-email: " + message);
+ process.exit(1);
+}
+
+function edit(label, from, to) {
+ if (source.includes(to)) {
+ console.log("patch-auto-verify-email: " + label + " already patched");
+ return;
+ }
+ const occurrences = source.split(from).length - 1;
+ if (occurrences !== 1) {
+ fail(label + ": expected 1 occurrence of the anchor, found " + occurrences);
+ }
+ source = source.replace(from, to);
+ console.log("patch-auto-verify-email: patched " + label);
+}
+
+// 1. Existing accounts: verify them when they log in.
+edit(
+ "login",
+ `\t\t\t\t\tR.auth = get_new_auth(R.user);
+\t\t\t\t\tawait tx_save(R.user);`,
+ `\t\t\t\t\t// Local: keep accounts verified without an email service.
+\t\t\t\t\tif (${autoVerify}) R.user.info.verified = true;
+\t\t\t\t\tR.auth = get_new_auth(R.user);
+\t\t\t\t\tawait tx_save(R.user);`,
+);
+
+// 2. New accounts: set the flag before the user document is written.
+edit(
+ "signup",
+ `\t\t\t\tblobs: ["info"],
+\t\t\t};
+\t\t\tR.auth = get_new_auth(R.user);`,
+ `\t\t\t\tblobs: ["info"],
+\t\t\t};
+\t\t\t// Local: new accounts are verified immediately without an email service.
+\t\t\tif (${autoVerify}) R.user.info.verified = true;
+\t\t\tR.auth = get_new_auth(R.user);`,
+);
+
+// 3. Signup: do not try to send a verification email when auto-verify is on.
+edit(
+ "signup email",
+ `\tset_cookie(args.res, options.cookie_key, get_id(R.user) + "-" + R.auth, domain.domain);
+\tsend_verification_email(domain, R.user);
+\targs.res.infs.push({ type: "success", message: phrase_html("server.api.signup_complete") });`,
+ `\tset_cookie(args.res, options.cookie_key, get_id(R.user) + "-" + R.auth, domain.domain);
+\tif (!(${autoVerify})) send_verification_email(domain, R.user);
+\targs.res.infs.push({ type: "success", message: phrase_html("server.api.signup_complete") });`,
+);
+
+// 4. Email changes: keep the account verified.
+edit(
+ "email change verification",
+ `\t\t\tif (gf(R.user, "verified", 0)) delete R.user.info.verified;`,
+ `\t\t\t// Local: keep the account verified after an email change.
+\t\t\tif (${autoVerify}) R.user.info.verified = true;
+\t\t\telse if (gf(R.user, "verified", 0)) delete R.user.info.verified;`,
+);
+
+// 5. Email changes: skip the verification email and its "re-sent" message.
+edit(
+ "email change email",
+ `\tif (R.failed) return { failed: true, reason: R.reason || "operation_failed" };
+\tsend_verification_email(domain, R.user);
+\targs.res.infs.push({ type: "success", message: phrase_html("server.api.email_changed_verification_email_re_sent_refresh_the_page") });`,
+ `\tif (R.failed) return { failed: true, reason: R.reason || "operation_failed" };
+\tif (${autoVerify}) {
+\t\targs.res.infs.push({ type: "success", message: phrase_html("server.api.setting_changed") });
+\t} else {
+\t\tsend_verification_email(domain, R.user);
+\t\targs.res.infs.push({ type: "success", message: phrase_html("server.api.email_changed_verification_email_re_sent_refresh_the_page") });
+\t}`,
+);
+
+fs.writeFileSync(file, source);
+console.log("patch-auto-verify-email: wrote " + file);
diff --git a/files/config/options.js b/files/config/options.js
index 2549f09..5ed5620 100644
--- a/files/config/options.js
+++ b/files/config/options.js
@@ -50,7 +50,11 @@ module.exports = {
port: 8090,
close_timeout: 4000,
ip_limit: 3,
- character_limit: 18,
+ character_limit: 3,
+ // Without an email service, mark new accounts as verified at signup and
+ // keep existing accounts verified on login. Set to false to restore the
+ // upstream verification flow (requires AWS SES).
+ auto_verify_email: true,
fast_sdk: 0,
machines: machines,
servers: servers,
diff --git a/verify_user.sh b/verify_user.sh
new file mode 100644
index 0000000..5a69570
--- /dev/null
+++ b/verify_user.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+# fallback script for mail verify
+
+set -euo pipefail
+
+JS='db.user.find({}, {"info.email": 1, "info.everification": 1}).forEach((u) =>
+ print("http://localhost:8090/ev/" + u._id + "/" + u.info.everification))'
+
+docker compose exec -T al-mongo mongosh --quiet adventureland --eval "$JS" \
+ | while IFS= read -r url; do
+ [[ "$url" == http* ]] || continue
+ code=$(curl -s -o /dev/null -w '%{http_code}' "$url")
+ echo "$code $url"
+ done
\ No newline at end of file