diff --git a/ts/form.ts b/ts/form.ts
index f278d86..b263481 100644
--- a/ts/form.ts
+++ b/ts/form.ts
@@ -4,6 +4,7 @@ import { _get, _post, toggleLoader, addLoader, removeLoader, toDateString } from
import { loadLangSelector } from "./modules/lang.js";
import { Validator, ValidatorConf, ValidatorRespDTO } from "./modules/validator.js";
import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js";
+import { Captcha } from "./modules/captcha.js";
interface formWindow extends Window {
invalidPassword: string;
@@ -172,35 +173,7 @@ if (!window.usernameEnabled) { usernameField.parentElement.remove(); usernameFie
const passwordField = document.getElementById("create-password") as HTMLInputElement;
const rePasswordField = document.getElementById("create-reenter-password") as HTMLInputElement;
-let captchaVerified = false;
-let captchaID = "";
-let captchaInput = document.getElementById("captcha-input") as HTMLInputElement;
-const captchaCheckbox = document.getElementById("captcha-success") as HTMLSpanElement;
-let prevCaptcha = "";
-
-let baseValidator = (oncomplete: (valid: boolean) => void): void => {
- if (window.captcha && !window.reCAPTCHA && (captchaInput.value != prevCaptcha)) {
- prevCaptcha = captchaInput.value;
- _post("/captcha/verify/" + window.code + "/" + captchaID + "/" + captchaInput.value, null, (req: XMLHttpRequest) => {
- if (req.readyState == 4) {
- if (req.status == 204) {
- captchaCheckbox.innerHTML = ``;
- captchaCheckbox.classList.add("~positive");
- captchaCheckbox.classList.remove("~critical");
- captchaVerified = true;
- } else {
- captchaCheckbox.innerHTML = ``;
- captchaCheckbox.classList.add("~critical");
- captchaCheckbox.classList.remove("~positive");
- captchaVerified = false;
- }
- _baseValidator(oncomplete, captchaVerified);
- }
- });
- } else {
- _baseValidator(oncomplete, captchaVerified);
- }
-}
+let captcha = new Captcha(window.code, window.captcha, window.reCAPTCHA);
function _baseValidator(oncomplete: (valid: boolean) => void, captchaValid: boolean): void {
if (window.emailRequired) {
@@ -228,6 +201,8 @@ function _baseValidator(oncomplete: (valid: boolean) => void, captchaValid: bool
oncomplete(true);
}
+let baseValidator = captcha.baseValidatorWrapper(_baseValidator);
+
interface GreCAPTCHA {
render: (container: HTMLDivElement, parameters: {
sitekey?: string,
@@ -273,29 +248,15 @@ interface sendDTO {
captcha_text?: string;
}
-const genCaptcha = () => {
- _get("/captcha/gen/"+window.code, null, (req: XMLHttpRequest) => {
- if (req.readyState == 4) {
- if (req.status == 200) {
- captchaID = req.response["id"];
- document.getElementById("captcha-img").innerHTML = `
-
- `;
- captchaInput.value = "";
- }
- }
- });
-};
-
if (window.captcha && !window.reCAPTCHA) {
- genCaptcha();
- (document.getElementById("captcha-regen") as HTMLSpanElement).onclick = genCaptcha;
- captchaInput.onkeyup = validator.validate;
+ captcha.generate();
+ (document.getElementById("captcha-regen") as HTMLSpanElement).onclick = captcha.generate;
+ captcha.input.onkeyup = validator.validate;
}
const create = (event: SubmitEvent) => {
event.preventDefault();
- if (window.captcha && !window.reCAPTCHA && !captchaVerified) {
+ if (window.captcha && !window.reCAPTCHA && !captcha.verified) {
}
addLoader(submitSpan);
@@ -330,8 +291,8 @@ const create = (event: SubmitEvent) => {
if (window.reCAPTCHA) {
send.captcha_text = grecaptcha.getResponse();
} else {
- send.captcha_id = captchaID;
- send.captcha_text = captchaInput.value;
+ send.captcha_id = captcha.captchaID;
+ send.captcha_text = captcha.input.value;
}
}
_post("/newUser", send, (req: XMLHttpRequest) => {
diff --git a/ts/modules/captcha.ts b/ts/modules/captcha.ts
new file mode 100644
index 0000000..ae9e5fc
--- /dev/null
+++ b/ts/modules/captcha.ts
@@ -0,0 +1,64 @@
+import { _get, _post } from "./common.js";
+
+export class Captcha {
+ enabled = true;
+ verified = false;
+ captchaID = "";
+ input = document.getElementById("captcha-input") as HTMLInputElement;
+ checkbox = document.getElementById("captcha-success") as HTMLSpanElement;
+ previous = "";
+ reCAPTCHA = false;
+ code = "";
+
+ get value(): string { return this.input.value; }
+
+ hasChanged = (): boolean => { return this.value != this.previous; }
+
+ baseValidatorWrapper = (_baseValidator: (oncomplete: (valid: boolean) => void, captchaValid: boolean) => void) => {
+ return (oncomplete: (valid: boolean) => void): void => {
+ if (this.enabled && !this.reCAPTCHA && this.hasChanged()) {
+ this.previous = this.value;
+ this.verify(() => {
+ _baseValidator(oncomplete, this.verified);
+ });
+ } else {
+ _baseValidator(oncomplete, this.verified);
+ }
+ };
+ };
+
+ verify = (callback: () => void) => _post("/captcha/verify/" + this.code + "/" + this.captchaID + "/" + this.input.value, null, (req: XMLHttpRequest) => {
+ if (req.readyState == 4) {
+ if (req.status == 204) {
+ this.checkbox.innerHTML = ``;
+ this.checkbox.classList.add("~positive");
+ this.checkbox.classList.remove("~critical");
+ this.verified = true;
+ } else {
+ this.checkbox.innerHTML = ``;
+ this.checkbox.classList.add("~critical");
+ this.checkbox.classList.remove("~positive");
+ this.verified = false;
+ }
+ callback();
+ }
+ });
+
+ generate = () => _get("/captcha/gen/"+this.code, null, (req: XMLHttpRequest) => {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ this.captchaID = req.response["id"];
+ document.getElementById("captcha-img").innerHTML = `
+
+ `;
+ this.input.value = "";
+ }
+ }
+ });
+
+ constructor(code: string, enabled: boolean, reCAPTCHA: boolean) {
+ this.code = code;
+ this.enabled = enabled;
+ this.reCAPTCHA = reCAPTCHA;
+ }
+}
diff --git a/ts/pwr.ts b/ts/pwr.ts
index d5a40a0..7cdaee6 100644
--- a/ts/pwr.ts
+++ b/ts/pwr.ts
@@ -28,6 +28,9 @@ interface formWindow extends Window {
userExpiryHours: number;
userExpiryMinutes: number;
userExpiryMessage: string;
+ captcha: boolean;
+ reCAPTCHA: boolean;
+ reCAPTCHASiteKey: string;
}
loadLangSelector("pwr");
diff --git a/views.go b/views.go
index 51ccca8..4896707 100644
--- a/views.go
+++ b/views.go
@@ -296,6 +296,9 @@ func (app *appContext) ResetPassword(gc *gin.Context) {
data["telegramEnabled"] = false
data["discordEnabled"] = false
data["matrixEnabled"] = false
+ data["captcha"] = app.config.Section("captcha").Key("enabled").MustBool(false)
+ data["reCAPTCHA"] = app.config.Section("captcha").Key("recaptcha").MustBool(false)
+ data["reCAPTCHASiteKey"] = app.config.Section("captcha").Key("recaptcha_site_key").MustString("")
gcHTML(gc, http.StatusOK, "form-loader.html", data)
return
}