mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-25 11:19:16 -06:00
fix: create stringService
This commit is contained in:
16
Docker/dist/docker-compose.yaml
vendored
16
Docker/dist/docker-compose.yaml
vendored
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
322
Server/service/stringService.js
Normal file
322
Server/service/stringService.js
Normal 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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
@@ -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('');
|
||||
};
|
||||
143
Server/utils/locales_en.json
Normal file
143
Server/utils/locales_en.json
Normal 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"
|
||||
}
|
||||
Reference in New Issue
Block a user