#!/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);