feat: Added a translation key for “Password is too weak (#6614)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Frank Elsinga <frank@elsinga.de>
This commit is contained in:
Cyril59310 2026-01-06 21:53:51 +01:00 committed by GitHub
parent 82c6b364af
commit fc832d0935
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 81 additions and 21 deletions

View File

@ -75,6 +75,7 @@ const gracefulShutdown = require("http-graceful-shutdown");
log.debug("server", "Importing prometheus-api-metrics");
const prometheusAPIMetrics = require("prometheus-api-metrics");
const { passwordStrength } = require("check-password-strength");
const TranslatableError = require("./translatable-error");
log.debug("server", "Importing 2FA Modules");
const notp = require("notp");
@ -673,7 +674,7 @@ let needSetup = false;
socket.on("setup", async (username, password, callback) => {
try {
if (passwordStrength(password).value === "Too weak") {
throw new Error("Password is too weak. It should contain alphabetic and numeric characters. It must be at least 6 characters in length.");
throw new TranslatableError("passwordTooWeak");
}
if ((await R.knex("user").count("id as count").first()).count !== 0) {
@ -697,6 +698,7 @@ let needSetup = false;
callback({
ok: false,
msg: e.message,
msgi18n: !!e.msgi18n,
});
}
});
@ -1410,7 +1412,7 @@ let needSetup = false;
}
if (passwordStrength(password.newPassword).value === "Too weak") {
throw new Error("Password is too weak. It should contain alphabetic and numeric characters. It must be at least 6 characters in length.");
throw new TranslatableError("passwordTooWeak");
}
let user = await doubleCheckPassword(socket, password.currentPassword);
@ -1429,6 +1431,7 @@ let needSetup = false;
callback({
ok: false,
msg: e.message,
msgi18n: !!e.msgi18n,
});
}
});

View File

@ -0,0 +1,17 @@
class TranslatableError extends Error {
/**
* Error whose message is a translation key.
* @augments Error
*/
/**
* Create a TranslatableError.
* @param {string} key - Translation key present in src/lang/en.json
*/
constructor(key) {
super(key);
this.msgi18n = true;
this.key = key;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = TranslatableError;

View File

@ -1304,6 +1304,7 @@
"Show this Maintenance Message on which Status Pages": "Show this Maintenance Message on which Status Pages",
"Endpoint": "Endpoint",
"Details": "Details",
"passwordTooWeak": "Password is too weak. It should contain alphabetic and numeric characters. It must be at least 6 characters in length.",
"TLS Alerts": "TLS Alerts",
"Expected TLS Alert": "Expected TLS Alert",
"None (Successful Connection)": "None (Successful Connection)",

View File

@ -20,6 +20,20 @@ function* walk(dir) {
}
}
/**
* Fallback to get start/end indices of a key within a line.
* @param {string} line - Line of text to search in.
* @param {string} key - Key to find.
* @returns {[number, number]} Array [start, end] representing the indices of the key in the line.
*/
function getStartEnd(line, key) {
let start = line.indexOf(key);
if (start === -1) {
start = 0;
}
return [ start, start + key.length ];
}
describe("Check Translations", () => {
it("should not have missing translation keys", () => {
const enTranslations = JSON.parse(fs.readFileSync("src/lang/en.json", "utf-8"));
@ -28,28 +42,53 @@ describe("Check Translations", () => {
/// this check is just to save on maintainer energy to explain this on every review ^^
const translationRegex = /\$t\(['"](?<key1>.*?)['"]\s*[,)]|i18n-t[^>]*\s+keypath="(?<key2>[^"]+)"/gd;
// detect server-side TranslatableError usage: new TranslatableError("key")
const translatableErrorRegex = /new\s+TranslatableError\(\s*['"](?<key3>[^'"]+)['"]\s*\)/g;
const missingKeys = [];
for (const filePath of walk("src")) {
if (filePath.endsWith(".vue") || filePath.endsWith(".js")) {
const lines = fs.readFileSync(filePath, "utf-8").split("\n");
lines.forEach((line, lineNum) => {
let match;
while ((match = translationRegex.exec(line)) !== null) {
const key = match.groups.key1 || match.groups.key2;
if (key && !enTranslations[key]) {
const [ start, end ] = match.groups.key1 ? match.indices.groups.key1 : match.indices.groups.key2;
missingKeys.push({
filePath,
lineNum: lineNum + 1,
key,
line: line,
start,
end,
});
const roots = [ "src", "server" ];
for (const root of roots) {
for (const filePath of walk(root)) {
if (filePath.endsWith(".vue") || filePath.endsWith(".js")) {
const lines = fs.readFileSync(filePath, "utf-8").split("\n");
lines.forEach((line, lineNum) => {
let match;
// front-end style keys ($t / i18n-t)
while ((match = translationRegex.exec(line)) !== null) {
const key = match.groups.key1 || match.groups.key2;
if (key && !enTranslations[key]) {
const [ start, end ] = getStartEnd(line, key);
missingKeys.push({
filePath,
lineNum: lineNum + 1,
key,
line: line,
start,
end,
});
}
}
}
});
// server-side TranslatableError usage
let m;
while ((m = translatableErrorRegex.exec(line)) !== null) {
const key3 = m.groups.key3;
if (key3 && !enTranslations[key3]) {
const [ start, end ] = getStartEnd(line, key3);
missingKeys.push({
filePath,
lineNum: lineNum + 1,
key: key3,
line: line,
start,
end,
});
}
}
});
}
}
}