fix: create stringService

This commit is contained in:
cihatata
2025-01-29 01:57:49 +03:00
committed by cihat
parent e18f1d9b85
commit 6fc0e31743
10 changed files with 760 additions and 86 deletions

View File

@@ -1,6 +1,8 @@
services:
client:
image: bluewaveuptime/uptime_client:latest
build:
context: ../../
dockerfile: Docker/dist/client.Dockerfile
restart: always
environment:
UPTIME_APP_API_BASE_URL: "http://localhost:5000/api/v1"
@@ -11,7 +13,9 @@ services:
depends_on:
- server
server:
image: bluewaveuptime/uptime_server:latest
build:
context: ../../
dockerfile: Docker/dist/server.Dockerfile
restart: always
ports:
- "5000:5000"
@@ -24,7 +28,9 @@ services:
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock:ro
redis:
image: bluewaveuptime/uptime_redis:latest
build:
context: ../../
dockerfile: Docker/dist/redis.Dockerfile
restart: always
ports:
- "6379:6379"
@@ -37,7 +43,9 @@ services:
retries: 5
start_period: 5s
mongodb:
image: bluewaveuptime/uptime_database_mongo:latest
build:
context: ../../
dockerfile: Docker/dist/mongoDB.Dockerfile
restart: always
volumes:
- ./mongo/data:/data/db

View File

@@ -16,11 +16,12 @@ import { handleValidationError, handleError } from "./controllerUtils.js";
const SERVICE_NAME = "authController";
class AuthController {
constructor(db, settingsService, emailService, jobQueue) {
constructor(db, settingsService, emailService, jobQueue, stringService) {
this.db = db;
this.settingsService = settingsService;
this.emailService = emailService;
this.jobQueue = jobQueue;
this.stringService = stringService;
}
/**
@@ -153,7 +154,7 @@ class AuthController {
// Compare password
const match = await user.comparePassword(password);
if (match !== true) {
const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD(req.language));
const error = new Error(this.stringService.authIncorrectPassword);
error.status = 401;
next(error);
return;
@@ -206,7 +207,7 @@ class AuthController {
if (!refreshToken) {
// No refresh token provided
const error = new Error(errorMessages.NO_REFRESH_TOKEN(req.language));
const error = new Error(this.stringService.noRefreshToken);
error.status = 401;
error.service = SERVICE_NAME;
error.method = "refreshAuthToken";
@@ -221,8 +222,8 @@ class AuthController {
// Invalid or expired refresh token, trigger logout
const errorMessage =
refreshErr.name === "TokenExpiredError"
? errorMessages.EXPIRED_REFRESH_TOKEN
: errorMessages.INVALID_REFRESH_TOKEN;
? this.stringService.expiredAuthToken
: this.stringService.invalidAuthToken;
const error = new Error(errorMessage);
error.status = 401;
error.service = SERVICE_NAME;
@@ -276,7 +277,7 @@ class AuthController {
// TODO is this neccessary any longer? Verify ownership middleware should handle this
if (req.params.userId !== req.user._id.toString()) {
const error = new Error(errorMessages.AUTH_UNAUTHORIZED(req.language));
const error = new Error(this.stringService.unauthorized);
error.status = 401;
error.service = SERVICE_NAME;
next(error);
@@ -300,7 +301,7 @@ class AuthController {
// If not a match, throw a 403
// 403 instead of 401 to avoid triggering axios interceptor
if (!match) {
const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD(req.language));
const error = new Error(this.stringService.authIncorrectPassword);
error.status = 403;
next(error);
return;

View File

@@ -76,6 +76,7 @@ import IORedis from "ioredis";
import TranslationService from './service/translationService.js';
import languageMiddleware from './middleware/languageMiddleware.js';
import StringService from './service/stringService.js';
const SERVICE_NAME = "Server";
const SHUTDOWN_TIMEOUT = 1000;
@@ -178,7 +179,8 @@ const startApp = async () => {
const networkService = new NetworkService(axios, ping, logger, http, Docker, net);
const statusService = new StatusService(db, logger);
const notificationService = new NotificationService(emailService, db, logger);
const translationService = new TranslationService(logger);
const translationService = new TranslationService(logger, networkService);
const stringService = new StringService(translationService);
const jobQueue = new JobQueue(
db,
@@ -200,7 +202,7 @@ const startApp = async () => {
ServiceRegistry.register(StatusService.SERVICE_NAME, statusService);
ServiceRegistry.register(NotificationService.SERVICE_NAME, notificationService);
ServiceRegistry.register(TranslationService.SERVICE_NAME, translationService);
ServiceRegistry.register(StringService.SERVICE_NAME, stringService);
await translationService.initialize();
@@ -217,7 +219,8 @@ const startApp = async () => {
ServiceRegistry.get(MongoDB.SERVICE_NAME),
ServiceRegistry.get(SettingsService.SERVICE_NAME),
ServiceRegistry.get(EmailService.SERVICE_NAME),
ServiceRegistry.get(JobQueue.SERVICE_NAME)
ServiceRegistry.get(JobQueue.SERVICE_NAME),
ServiceRegistry.get(StringService.SERVICE_NAME)
);
const monitorController = new MonitorController(
@@ -279,7 +282,7 @@ const startApp = async () => {
app.use(cors());
app.use(express.json());
app.use(helmet());
app.use(languageMiddleware);
app.use(languageMiddleware(stringService, translationService));
// Swagger UI
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec));

View File

@@ -1,7 +1,9 @@
const languageMiddleware = (req, res, next) => {
const languageMiddleware = (stringService, translationService) => (req, res, next) => {
const acceptLanguage = req.headers['accept-language'] || 'en';
const language = acceptLanguage.split(',')[0].slice(0, 2).toLowerCase();
req.language = acceptLanguage.split(',')[0].slice(0, 2).toLowerCase();
translationService.setLanguage(language);
stringService.setLanguage(language);
next();
};

View File

@@ -13,6 +13,8 @@ const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push";
*/
class NetworkService {
static SERVICE_NAME = SERVICE_NAME;
static POEDITOR_BASE_URL = 'https://api.poeditor.com/v2';
constructor(axios, ping, logger, http, Docker, net) {
this.TYPE_PING = "ping";
this.TYPE_HTTP = "http";
@@ -30,6 +32,17 @@ class NetworkService {
this.http = http;
this.Docker = Docker;
this.net = net;
this.apiToken = process.env.POEDITOR_API_TOKEN;
this.projectId = process.env.POEDITOR_PROJECT_ID;
if (!this.apiToken || !this.projectId) {
this.logger.error({
message: 'POEditor API token or project ID is missing in environment variables',
service: this.SERVICE_NAME,
method: 'constructor'
});
}
}
/**
@@ -398,6 +411,95 @@ class NetworkService {
return this.handleUnsupportedType(type);
}
}
async getPoEditorLanguages() {
try {
const params = new URLSearchParams();
params.append('api_token', this.apiToken);
params.append('id', this.projectId);
const response = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/languages/list`, params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
return response.data.result.languages.map(lang => lang.code);
} catch (error) {
error.service = this.SERVICE_NAME;
error.method = "getPoEditorLanguages";
throw error;
}
}
async exportPoEditorTranslations(language) {
try {
const params = new URLSearchParams();
params.append('api_token', this.apiToken);
params.append('id', this.projectId);
params.append('language', language);
params.append('type', 'key_value_json');
const exportResponse = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/projects/export`, params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const { url } = exportResponse.data.result;
const translationsResponse = await this.axios.get(url);
return translationsResponse.data;
} catch (error) {
error.service = this.SERVICE_NAME;
error.method = "exportPoEditorTranslations";
throw error;
}
}
async getPoEditorTerms() {
try {
const params = new URLSearchParams();
params.append('api_token', this.apiToken);
params.append('id', this.projectId);
const response = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/terms/list`, params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
return response.data.result?.terms?.map(term => term.term.trim()) || [];
} catch (error) {
error.service = this.SERVICE_NAME;
error.method = "getPoEditorTerms";
throw error;
}
}
async addPoEditorTerms(terms) {
try {
const formattedTerms = terms.map(termObj => ({
term: Object.keys(termObj)[0]
}));
const params = new URLSearchParams();
params.append('api_token', this.apiToken);
params.append('id', this.projectId);
params.append('data', JSON.stringify(formattedTerms));
const response = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/terms/add`, params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
return response.data;
} catch (error) {
error.service = this.SERVICE_NAME;
error.method = "addPoEditorTerms";
throw error;
}
}
}
export default NetworkService;

View File

@@ -0,0 +1,322 @@
class StringService {
static SERVICE_NAME = "StringService";
constructor(translationService) {
if (StringService.instance) {
return StringService.instance;
}
this.translationService = translationService;
this._language = 'en'; // default language
StringService.instance = this;
}
setLanguage(language) {
this._language = language;
}
get language() {
return this._language;
}
// Auth Messages
get dontHaveAccount() {
return this.translationService.getTranslation('dontHaveAccount');
}
get email() {
return this.translationService.getTranslation('email');
}
get forgotPassword() {
return this.translationService.getTranslation('forgotPassword');
}
get password() {
return this.translationService.getTranslation('password');
}
get signUp() {
return this.translationService.getTranslation('signUp');
}
get submit() {
return this.translationService.getTranslation('submit');
}
get title() {
return this.translationService.getTranslation('title');
}
get continue() {
return this.translationService.getTranslation('continue');
}
get enterEmail() {
return this.translationService.getTranslation('enterEmail');
}
get authLoginTitle() {
return this.translationService.getTranslation('authLoginTitle');
}
get authLoginEnterPassword() {
return this.translationService.getTranslation('authLoginEnterPassword');
}
get commonPassword() {
return this.translationService.getTranslation('commonPassword');
}
get commonBack() {
return this.translationService.getTranslation('commonBack');
}
get authForgotPasswordTitle() {
return this.translationService.getTranslation('authForgotPasswordTitle');
}
get authForgotPasswordResetPassword() {
return this.translationService.getTranslation('authForgotPasswordResetPassword');
}
get createPassword() {
return this.translationService.getTranslation('createPassword');
}
get createAPassword() {
return this.translationService.getTranslation('createAPassword');
}
get authRegisterAlreadyHaveAccount() {
return this.translationService.getTranslation('authRegisterAlreadyHaveAccount');
}
get commonAppName() {
return this.translationService.getTranslation('commonAppName');
}
get authLoginEnterEmail() {
return this.translationService.getTranslation('authLoginEnterEmail');
}
get authRegisterTitle() {
return this.translationService.getTranslation('authRegisterTitle');
}
get monitorGetAll() {
return this.translationService.getTranslation('monitorGetAll');
}
get monitorGetById() {
return this.translationService.getTranslation('monitorGetById');
}
get monitorCreate() {
return this.translationService.getTranslation('monitorCreate');
}
get monitorEdit() {
return this.translationService.getTranslation('monitorEdit');
}
get monitorDelete() {
return this.translationService.getTranslation('monitorDelete');
}
get monitorPause() {
return this.translationService.getTranslation('monitorPause');
}
get monitorResume() {
return this.translationService.getTranslation('monitorResume');
}
get monitorDemoAdded() {
return this.translationService.getTranslation('monitorDemoAdded');
}
get monitorStatsById() {
return this.translationService.getTranslation('monitorStatsById');
}
get monitorCertificate() {
return this.translationService.getTranslation('monitorCertificate');
}
// Maintenance Window Messages
get maintenanceWindowCreate() {
return this.translationService.getTranslation('maintenanceWindowCreate');
}
get maintenanceWindowGetById() {
return this.translationService.getTranslation('maintenanceWindowGetById');
}
get maintenanceWindowGetByTeam() {
return this.translationService.getTranslation('maintenanceWindowGetByTeam');
}
get maintenanceWindowDelete() {
return this.translationService.getTranslation('maintenanceWindowDelete');
}
get maintenanceWindowEdit() {
return this.translationService.getTranslation('maintenanceWindowEdit');
}
// Error Messages
get unknownError() {
return this.translationService.getTranslation('unknownError');
}
get friendlyError() {
return this.translationService.getTranslation('friendlyError');
}
get authIncorrectPassword() {
return this.translationService.getTranslation('authIncorrectPassword');
}
get unauthorized() {
return this.translationService.getTranslation('unauthorized');
}
get authAdminExists() {
return this.translationService.getTranslation('authAdminExists');
}
get authInviteNotFound() {
return this.translationService.getTranslation('authInviteNotFound');
}
get unknownService() {
return this.translationService.getTranslation('unknownService');
}
get noAuthToken() {
return this.translationService.getTranslation('noAuthToken');
}
get invalidAuthToken() {
return this.translationService.getTranslation('invalidAuthToken');
}
get expiredAuthToken() {
return this.translationService.getTranslation('expiredAuthToken');
}
// Queue Messages
get queueGetMetrics() {
return this.translationService.getTranslation('queueGetMetrics');
}
get queueAddJob() {
return this.translationService.getTranslation('queueAddJob');
}
get queueObliterate() {
return this.translationService.getTranslation('queueObliterate');
}
// Job Queue Messages
get jobQueueDeleteJobSuccess() {
return this.translationService.getTranslation('jobQueueDeleteJobSuccess');
}
get jobQueuePauseJob() {
return this.translationService.getTranslation('jobQueuePauseJob');
}
get jobQueueResumeJob() {
return this.translationService.getTranslation('jobQueueResumeJob');
}
// Status Page Messages
get statusPageByUrl() {
return this.translationService.getTranslation('statusPageByUrl');
}
get statusPageCreate() {
return this.translationService.getTranslation('statusPageCreate');
}
get statusPageNotFound() {
return this.translationService.getTranslation('statusPageNotFound');
}
get statusPageUrlNotUnique() {
return this.translationService.getTranslation('statusPageUrlNotUnique');
}
// Docker Messages
get dockerFail() {
return this.translationService.getTranslation('dockerFail');
}
get dockerNotFound() {
return this.translationService.getTranslation('dockerNotFound');
}
get dockerSuccess() {
return this.translationService.getTranslation('dockerSuccess');
}
// Port Messages
get portFail() {
return this.translationService.getTranslation('portFail');
}
get portSuccess() {
return this.translationService.getTranslation('portSuccess');
}
// Alert Messages
get alertCreate() {
return this.translationService.getTranslation('alertCreate');
}
get alertGetByUser() {
return this.translationService.getTranslation('alertGetByUser');
}
get alertGetByMonitor() {
return this.translationService.getTranslation('alertGetByMonitor');
}
get alertGetById() {
return this.translationService.getTranslation('alertGetById');
}
get alertEdit() {
return this.translationService.getTranslation('alertEdit');
}
get alertDelete() {
return this.translationService.getTranslation('alertDelete');
}
getDeletedCount(count) {
return this.translationService.getTranslation('deletedCount')
.replace('{count}', count);
}
get pingSuccess() {
return this.translationService.getTranslation('pingSuccess');
}
get getAppSettings() {
return this.translationService.getTranslation('getAppSettings');
}
get updateAppSettings() {
return this.translationService.getTranslation('updateAppSettings');
}
getDbFindMonitorById(monitorId) {
return this.translationService.getTranslation('dbFindMonitorById')
.replace('${monitorId}', monitorId);
}
}
export default StringService;

View File

@@ -1,27 +1,34 @@
import axios from 'axios';
import fs from 'fs';
import path from 'path';
import { formattedKey } from '../utils/formattedKey.js';
class TranslationService {
static SERVICE_NAME = 'TranslationService';
constructor(logger) {
constructor(logger, networkService) {
this.logger = logger;
this.networkService = networkService;
this.translations = {};
this.apiToken = process.env.POEDITOR_API_TOKEN;
this.projectId = process.env.POEDITOR_PROJECT_ID;
this.baseUrl = 'https://api.poeditor.com/v2';
this._language = 'en';
this.localesDir = path.join(process.cwd(), 'locales');
}
setLanguage(language) {
this._language = language;
}
get language() {
return this._language;
}
async initialize() {
try {
const loadedFromFiles = await this.loadFromFiles();
if (!loadedFromFiles) {
await this.loadTranslations();
}
// Yeni eklenen terimleri POEditor'e gönder
await this.syncTermsWithPOEditor();
} catch (error) {
this.logger.error({
message: error.message,
@@ -70,16 +77,47 @@ class TranslationService {
}
async loadTranslations() {
let hasError = false;
try {
const languages = await this.getLanguages();
for (const language of languages) {
const translations = await this.exportTranslations(language);
this.translations[language] = translations;
try {
const translations = await this.exportTranslations(language);
this.translations[language] = translations;
} catch (error) {
hasError = true;
this.logger.error({
message: `Failed to fetch translations from POEditor for language ${language}: ${error.message}`,
service: 'TranslationService',
method: 'loadTranslations',
stack: error.stack
});
}
}
if (hasError || Object.keys(this.translations[this._language]).length === 0) {
this.logger.error({
message: 'Failed to fetch translations from POEditor, using locales_en.json',
service: 'TranslationService',
method: 'loadTranslations'
});
// Load translations from locales_en.json in utils directory
const utilsPath = path.join(process.cwd(), 'utils');
const utilsFilePath = path.join(utilsPath, 'locales_en.json');
if (fs.existsSync(utilsFilePath)) {
const content = fs.readFileSync(utilsFilePath, 'utf8');
this.translations['en'] = JSON.parse(content);
} else {
throw new Error('locales_en.json file not found');
}
}
await this.saveTranslations();
} catch (error) {
hasError = true;
this.logger.error({
message: error.message,
service: 'TranslationService',
@@ -91,17 +129,7 @@ class TranslationService {
async getLanguages() {
try {
const params = new URLSearchParams();
params.append('api_token', this.apiToken);
params.append('id', this.projectId);
const response = await axios.post(`${this.baseUrl}/languages/list`, params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
return response.data.result.languages.map(lang => lang.code);
return await this.networkService.getPoEditorLanguages();
} catch (error) {
this.logger.error({
message: error.message,
@@ -115,22 +143,7 @@ class TranslationService {
async exportTranslations(language) {
try {
const params = new URLSearchParams();
params.append('api_token', this.apiToken);
params.append('id', this.projectId);
params.append('language', language);
params.append('type', 'key_value_json');
const exportResponse = await axios.post(`${this.baseUrl}/projects/export`, params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const { url } = exportResponse.data.result;
const translationsResponse = await axios.get(url);
return translationsResponse.data;
return await this.networkService.exportPoEditorTranslations(language);
} catch (error) {
this.logger.error({
message: error.message,
@@ -153,6 +166,11 @@ class TranslationService {
fs.writeFileSync(filePath, JSON.stringify(translations, null, 2));
}
const utilsPath = path.join(process.cwd(), 'utils');
const enTranslations = this.translations['en'] || {};
const utilsFilePath = path.join(utilsPath, 'locales_en.json');
fs.writeFileSync(utilsFilePath, JSON.stringify(enTranslations, null, 2));
this.logger.info({
message: 'Translations saved to files successfully',
service: 'TranslationService',
@@ -168,10 +186,11 @@ class TranslationService {
}
}
getTranslation(key, language = 'en') {
const formattedKeyText = formattedKey(key);
getTranslation(key) {
let language = this._language;
try {
return this.translations[language]?.[formattedKeyText] || this.translations['en']?.[formattedKeyText] || formattedKeyText;
return this.translations[language]?.[key] || this.translations['en']?.[key] || key;
} catch (error) {
this.logger.error({
message: error.message,
@@ -182,6 +201,94 @@ class TranslationService {
return key;
}
}
async getTermsFromPOEditor() {
try {
return await this.networkService.getPoEditorTerms();
} catch (error) {
this.logger.error({
message: error.message,
service: 'TranslationService',
method: 'getTermsFromPOEditor',
stack: error.stack
});
return [];
}
}
async addTermsToPOEditor(terms) {
try {
if (!terms.length) return;
const response = await this.networkService.addPoEditorTerms(terms);
if (response.response?.status === 'fail') {
throw new Error(response.response.message || 'Failed to add terms to POEditor');
}
this.logger.info({
message: `${terms.length} new terms added to POEditor`,
service: 'TranslationService',
method: 'addTermsToPOEditor',
response: response
});
return response;
} catch (error) {
this.logger.error({
message: `Failed to add terms to POEditor: ${error.message}`,
service: 'TranslationService',
method: 'addTermsToPOEditor',
stack: error.stack,
terms: terms
});
throw error;
}
}
async syncTermsWithPOEditor() {
try {
const utilsPath = path.join(process.cwd(), 'utils');
const utilsFilePath = path.join(utilsPath, 'locales_en.json');
const enTranslations = JSON.parse(fs.readFileSync(utilsFilePath, 'utf8'));
const localTerms = Object.keys(enTranslations)
.map(term => term);
const poeditorTerms = await this.getTermsFromPOEditor();
const newTerms = localTerms?.filter(term => !poeditorTerms?.includes(term));
this.logger.info({
message: `Comparison results - New terms found: ${newTerms.length}`,
sampleNewTerms: newTerms.slice(0, 5),
service: 'TranslationService',
method: 'syncTermsWithPOEditor'
});
if (newTerms.length > 0) {
const formattedTerms = newTerms.map(term => ({
[term]: enTranslations[term] || '',
}));
await this.addTermsToPOEditor(formattedTerms);
} else {
this.logger.info({
message: 'No new terms found to synchronize',
service: 'TranslationService',
method: 'syncTermsWithPOEditor'
});
}
} catch (error) {
this.logger.error({
message: error.message,
service: 'TranslationService',
method: 'syncTermsWithPOEditor',
stack: error.stack
});
}
}
}
export default TranslationService;

View File

@@ -8,10 +8,10 @@ import {
import { successMessages } from "../../utils/messages.js";
import sinon from "sinon";
describe("Queue Controller - getMetrics", function() {
describe("Queue Controller - getMetrics", function () {
let req, res, next;
beforeEach(function() {
beforeEach(function () {
req = {
headers: {},
params: {},
@@ -32,14 +32,14 @@ describe("Queue Controller - getMetrics", function() {
sinon.restore();
});
it("should throw an error if getMetrics throws an error", async function() {
it("should throw an error if getMetrics throws an error", async function () {
req.jobQueue.getMetrics.throws(new Error("getMetrics error"));
await getMetrics(req, res, next);
expect(next.firstCall.args[0]).to.be.an("error");
expect(next.firstCall.args[0].message).to.equal("getMetrics error");
});
it("should return a success message and data if getMetrics is successful", async function() {
it("should return a success message and data if getMetrics is successful", async function () {
const data = { data: "metrics" };
req.jobQueue.getMetrics.returns(data);
await getMetrics(req, res, next);
@@ -52,10 +52,10 @@ describe("Queue Controller - getMetrics", function() {
});
});
describe("Queue Controller - getJobs", function() {
describe("Queue Controller - getJobs", function () {
let req, res, next;
beforeEach(function() {
beforeEach(function () {
req = {
headers: {},
params: {},
@@ -76,14 +76,14 @@ describe("Queue Controller - getJobs", function() {
sinon.restore();
});
it("should reject with an error if getJobs throws an error", async function() {
it("should reject with an error if getJobs throws an error", async function () {
req.jobQueue.getJobStats.throws(new Error("getJobs error"));
await getJobs(req, res, next);
expect(next.firstCall.args[0]).to.be.an("error");
expect(next.firstCall.args[0].message).to.equal("getJobs error");
});
it("should return a success message and data if getJobs is successful", async function() {
it("should return a success message and data if getJobs is successful", async function () {
const data = { data: "jobs" };
req.jobQueue.getJobStats.returns(data);
await getJobs(req, res, next);
@@ -96,10 +96,10 @@ describe("Queue Controller - getJobs", function() {
});
});
describe("Queue Controller - addJob", function() {
describe("Queue Controller - addJob", function () {
let req, res, next;
beforeEach(function() {
beforeEach(function () {
req = {
headers: {},
params: {},
@@ -120,14 +120,14 @@ describe("Queue Controller - addJob", function() {
sinon.restore();
});
it("should reject with an error if addJob throws an error", async function() {
it("should reject with an error if addJob throws an error", async function () {
req.jobQueue.addJob.throws(new Error("addJob error"));
await addJob(req, res, next);
expect(next.firstCall.args[0]).to.be.an("error");
expect(next.firstCall.args[0].message).to.equal("addJob error");
});
it("should return a success message if addJob is successful", async function() {
it("should return a success message if addJob is successful", async function () {
req.jobQueue.addJob.resolves();
await addJob(req, res, next);
expect(res.status.firstCall.args[0]).to.equal(200);
@@ -138,10 +138,10 @@ describe("Queue Controller - addJob", function() {
});
});
describe("Queue Controller - obliterateQueue", function() {
describe("Queue Controller - obliterateQueue", function () {
let req, res, next;
beforeEach(function() {
beforeEach(function () {
req = {
headers: {},
params: {},
@@ -162,14 +162,14 @@ describe("Queue Controller - obliterateQueue", function() {
sinon.restore();
});
it("should reject with an error if obliterateQueue throws an error", async function() {
it("should reject with an error if obliterateQueue throws an error", async function () {
req.jobQueue.obliterate.throws(new Error("obliterateQueue error"));
await obliterateQueue(req, res, next);
expect(next.firstCall.args[0]).to.be.an("error");
expect(next.firstCall.args[0].message).to.equal("obliterateQueue error");
});
it("should return a success message if obliterateQueue is successful", async function() {
it("should return a success message if obliterateQueue is successful", async function () {
req.jobQueue.obliterate.resolves();
await obliterateQueue(req, res, next);
expect(res.status.firstCall.args[0]).to.equal(200);

View File

@@ -1,14 +0,0 @@
/**
* Converts a snake_case or SCREAMING_SNAKE_CASE key to camelCase
* Example: AUTH_INCORRECT_PASSWORD -> authIncorrectPassword
* @param {string} key - The key to format
* @returns {string} - The formatted key in camelCase
*/
export const formattedKey = (key) => {
return key.toLowerCase()
.split('_')
.map((word, index) =>
index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)
)
.join('');
};

View File

@@ -0,0 +1,143 @@
{
"dontHaveAccount": "Don't have account",
"email": "E-mail",
"forgotPassword": "Forgot Password",
"password": "password",
"signUp": "Sign up",
"submit": "Submit",
"title": "Title",
"continue": "Continue",
"enterEmail": "Enter your email",
"authLoginTitle": "Log In",
"authLoginEnterPassword": "Enter your password",
"commonPassword": "Password",
"commonBack": "Back",
"authForgotPasswordTitle": "Forgot password?",
"authForgotPasswordResetPassword": "Reset password",
"createPassword": "Create your password",
"createAPassword": "Create a password",
"authRegisterAlreadyHaveAccount": "Already have an account?",
"commonAppName": "BlueWave Uptime",
"authLoginEnterEmail": "Enter your email",
"authRegisterTitle": "Create an account",
"authRegisterStepOneTitle": "Create your account",
"authRegisterStepOneDescription": "Enter your details to get started",
"authRegisterStepTwoTitle": "Set up your profile",
"authRegisterStepTwoDescription": "Tell us more about yourself",
"authRegisterStepThreeTitle": "Almost done!",
"authRegisterStepThreeDescription": "Review your information",
"authForgotPasswordDescription": "No worries, we'll send you reset instructions.",
"authForgotPasswordSendInstructions": "Send instructions",
"authForgotPasswordBackTo": "Back to",
"authCheckEmailTitle": "Check your email",
"authCheckEmailDescription": "We sent a password reset link to {{email}}",
"authCheckEmailResendEmail": "Resend email",
"authCheckEmailBackTo": "Back to",
"goBackTo": "Go back to",
"authCheckEmailDidntReceiveEmail": "Didn't receive the email?",
"authCheckEmailClickToResend": "Click to resend",
"authSetNewPasswordTitle": "Set new password",
"authSetNewPasswordDescription": "Your new password must be different from previously used passwords.",
"authSetNewPasswordNewPassword": "New password",
"authSetNewPasswordConfirmPassword": "Confirm password",
"confirmPassword": "Confirm your password",
"authSetNewPasswordResetPassword": "Reset password",
"authSetNewPasswordBackTo": "Back to",
"authPasswordMustBeAtLeast": "Must be at least",
"authPasswordCharactersLong": "8 characters long",
"authPasswordMustContainAtLeast": "Must contain at least",
"authPasswordSpecialCharacter": "one special character",
"authPasswordOneNumber": "one number",
"authPasswordUpperCharacter": "one upper character",
"authPasswordLowerCharacter": "one lower character",
"authPasswordConfirmAndPassword": "Confirm password and password",
"authPasswordMustMatch": "must match",
"friendlyError": "Something went wrong...",
"unknownError": "An unknown error occurred",
"unauthorized": "Unauthorized access",
"authAdminExists": "Admin already exists",
"authInviteNotFound": "Invite not found",
"unknownService": "Unknown service",
"noAuthToken": "No auth token provided",
"invalidAuthToken": "Invalid auth token",
"expiredAuthToken": "Token expired",
"noRefreshToken": "No refresh token provided",
"invalidRefreshToken": "Invalid refresh token",
"expiredRefreshToken": "Refresh token expired",
"requestNewAccessToken": "Request new access token",
"invalidPayload": "Invalid payload",
"verifyOwnerNotFound": "Document not found",
"verifyOwnerUnauthorized": "Unauthorized access",
"insufficientPermissions": "Insufficient permissions",
"dbUserExists": "User already exists",
"dbUserNotFound": "User not found",
"dbTokenNotFound": "Token not found",
"dbResetPasswordBadMatch": "New password must be different from old password",
"dbFindMonitorById": "Monitor with id ${monitorId} not found",
"dbDeleteChecks": "No checks found for monitor with id ${monitorId}",
"authIncorrectPassword": "Incorrect password",
"authUnauthorized": "Unauthorized access",
"monitorGetById": "Monitor not found",
"monitorGetByUserId": "No monitors found for user",
"jobQueueWorkerClose": "Error closing worker",
"jobQueueDeleteJob": "Job not found in queue",
"jobQueueObliterate": "Error obliterating queue",
"pingCannotResolve": "No response",
"statusPageNotFound": "Status page not found",
"statusPageUrlNotUnique": "Status page url must be unique",
"dockerFail": "Failed to fetch Docker container information",
"dockerNotFound": "Docker container not found",
"portFail": "Failed to connect to port",
"alertCreate": "Alert created successfully",
"alertGetByUser": "Got alerts successfully",
"alertGetByMonitor": "Got alerts by Monitor successfully",
"alertGetById": "Got alert by Id successfully",
"alertEdit": "Alert edited successfully",
"alertDelete": "Alert deleted successfully",
"authCreateUser": "User created successfully",
"authLoginUser": "User logged in successfully",
"authLogoutUser": "User logged out successfully",
"authUpdateUser": "User updated successfully",
"authCreateRecoveryToken": "Recovery token created successfully",
"authVerifyRecoveryToken": "Recovery token verified successfully",
"authResetPassword": "Password reset successfully",
"authAdminCheck": "Admin check completed successfully",
"authDeleteUser": "User deleted successfully",
"authTokenRefreshed": "Auth token is refreshed",
"authGetAllUsers": "Got all users successfully",
"inviteIssued": "Invite sent successfully",
"inviteVerified": "Invite verified successfully",
"checkCreate": "Check created successfully",
"checkGet": "Got checks successfully",
"checkDelete": "Checks deleted successfully",
"checkUpdateTtl": "Checks TTL updated successfully",
"monitorGetAll": "Got all monitors successfully",
"monitorStatsById": "Got monitor stats by Id successfully",
"monitorGetByIdSuccess": "Got monitor by Id successfully",
"monitorGetByTeamId": "Got monitors by Team Id successfully",
"monitorGetByUserIdSuccess": "Got monitor for ${userId} successfully",
"monitorCreate": "Monitor created successfully",
"monitorDelete": "Monitor deleted successfully",
"monitorEdit": "Monitor edited successfully",
"monitorCertificate": "Got monitor certificate successfully",
"monitorDemoAdded": "Successfully added demo monitors",
"queueGetMetrics": "Got metrics successfully",
"queueAddJob": "Job added successfully",
"queueObliterate": "Queue obliterated",
"jobQueueDeleteJobSuccess": "Job removed successfully",
"jobQueuePauseJob": "Job paused successfully",
"jobQueueResumeJob": "Job resumed successfully",
"maintenanceWindowGetById": "Got Maintenance Window by Id successfully",
"maintenanceWindowCreate": "Maintenance Window created successfully",
"maintenanceWindowGetByTeam": "Got Maintenance Windows by Team successfully",
"maintenanceWindowDelete": "Maintenance Window deleted successfully",
"maintenanceWindowEdit": "Maintenance Window edited successfully",
"pingSuccess": "Success",
"getAppSettings": "Got app settings successfully",
"updateAppSettings": "Updated app settings successfully",
"statusPageByUrl": "Got status page by url successfully",
"statusPageCreate": "Status page created successfully",
"newTermsAdded": "New terms added to POEditor",
"dockerSuccess": "Docker container status fetched successfully",
"portSuccess": "Port connected successfully"
}