diff --git a/common/controllers/sync.js b/common/controllers/sync.js index 522efc60..1fbf5d56 100644 --- a/common/controllers/sync.js +++ b/common/controllers/sync.js @@ -823,6 +823,21 @@ module.exports = function (Sync) { } } + // outbreaks chosen by the user; when not sent, the ones configured for the upstream server are used + let requestedOutbreakIDs; + if (data.outbreakIDs !== undefined && data.outbreakIDs !== null) { + if ( + !Array.isArray(data.outbreakIDs) || + data.outbreakIDs.some((outbreakId) => typeof outbreakId !== 'string' || !outbreakId) + ) { + return callback(app.utils.apiError.getError('REQUEST_VALIDATION_ERROR', { + errorMessages: 'Property "outbreakIDs" must be a list of outbreak ids' + })); + } + + requestedOutbreakIDs = _.uniq(data.outbreakIDs); + } + // initialize flag to know if the callback function was already called // need to do this as we will call it and then continue doing some actions let callbackCalled = false; @@ -836,6 +851,18 @@ module.exports = function (Sync) { // initialize variable for caching the system settings let systemSettings; + /** + * Save the step the sync reached, so it can be displayed while it is in progress + */ + const setSyncStep = (step) => { + syncLogEntry.syncStep = step; + return syncLogEntry + .save(options) + .catch((err) => { + app.logger.debug(`Sync ${syncLogEntry.id}: Error updating sync log entry step. ${err}`); + }); + }; + // check if the received upstream server URL matches one from the configured upstream servers app.models.systemSettings .findOne() @@ -879,7 +906,8 @@ module.exports = function (Sync) { return app.models.syncLog.create({ syncServerUrl: upstreamServerEntry.url, actionStartDate: localizationHelper.now().toDate(), - status: 'LNG_SYNC_STATUS_IN_PROGRESS' + status: 'LNG_SYNC_STATUS_IN_PROGRESS', + syncStep: 'CONNECT' }, options); }) .then(function (syncLog) { @@ -896,26 +924,102 @@ module.exports = function (Sync) { app.logger.debug(`Started sync ${syncLogEntry.id}`); - // continue sync - // get available outbreakIDs for the client - return Sync.getAvailableOutbreaksIDs(upstreamServerEntry, syncLogEntry); + // check if the server can be reached & if it accepts the credentials, so the user knows which step failed + // Note: the check only explains a failure; it never stops a sync that would work + return upstreamServerCheck + .check({ + url: upstreamServerEntry.url, + clientId: _.get(upstreamServerEntry, 'credentials.clientId'), + clientSecret: _.get(upstreamServerEntry, 'credentials.clientSecret') + }) + .catch(function (err) { + app.logger.debug(`Sync ${syncLogEntry.id}: Couldn't check the upstream server. ${err}`); + return null; + }) + .then(function (checkResult) { + // the server was reached + const serverOffline = checkResult && !checkResult.server.online; + return (serverOffline ? Promise.resolve() : setSyncStep('CREDENTIALS')) + .then(function () { + // get available outbreakIDs for the client + return Sync.getAvailableOutbreaksIDs(upstreamServerEntry, syncLogEntry); + }) + .catch(function (err) { + // explain why + const failedCheck = serverOffline ? + checkResult.server : ( + checkResult && checkResult.credentials && !checkResult.credentials.valid ? + checkResult.credentials : + null + ); + if (failedCheck) { + syncLogEntry.syncStepErrorCode = failedCheck.errorCode; + syncLogEntry.syncStepErrorDetail = failedCheck.code; + } + + throw err; + }); + }); }) .then(function (outbreakIDs) { + // send only the chosen outbreaks, if the upstream server accepts them (no outbreaks means all of them) + const chosenOutbreakIDs = requestedOutbreakIDs !== undefined ? + requestedOutbreakIDs : + _.uniq(upstreamServerEntry.outbreakIDs || []); + if (chosenOutbreakIDs.length) { + const acceptedOutbreakIDs = outbreakIDs.length ? + chosenOutbreakIDs.filter((outbreakId) => outbreakIDs.includes(outbreakId)) : + chosenOutbreakIDs; + if (!acceptedOutbreakIDs.length) { + throw app.utils.apiError.getError('REQUEST_VALIDATION_ERROR', { + errorMessages: 'None of the chosen outbreaks is accepted by the upstream server' + }); + } + + outbreakIDs = acceptedOutbreakIDs; + } + app.logger.debug(`Sync ${syncLogEntry.id}: Sync will be done for ${outbreakIDs.length ? ('the following outbreaks: ' + outbreakIDs.join(', ')) : 'all the outbreaks in the system'}`); // save retrieve outbreak IDs on the sync log entry syncLogEntry.outbreakIDs = outbreakIDs; // check if the outbreaks with the given IDs were ever successfully synced with the upstream server // we will only sync daca updated from the last sync + // a previous sync can be used only if it sent all the outbreaks that are sent now, otherwise what was changed in the others would never be sent + // (e.g. an outbreak was added to the ones synced with the server, or the previous sync sent only some outbreaks) + const sentAllOutbreaksConditions = [{ + outbreakIDs: { + $exists: false + } + }, { + outbreakIDs: null + }, { + outbreakIDs: { + $size: 0 + } + }]; return app.models.syncLog - .findOne({ - where: { - syncServerUrl: upstreamServerEntry.url, - status: 'LNG_SYNC_STATUS_SUCCESS' + .rawFind({ + syncServerUrl: upstreamServerEntry.url, + status: 'LNG_SYNC_STATUS_SUCCESS', + $or: outbreakIDs.length ? + sentAllOutbreaksConditions.concat([{ + outbreakIDs: { + $all: outbreakIDs + } + }]) : + sentAllOutbreaksConditions + }, { + projection: { + actionStartDate: 1 }, - order: 'actionStartDate DESC' + sort: { + actionStartDate: -1 + }, + limit: 1 }) - .then(function (lastSyncLogEntry) { + .then(function (lastSyncLogEntries) { + const lastSyncLogEntry = lastSyncLogEntries[0]; // determine the date from which we will sync the data // by default it is the start of the last successful sync (or all data if there wasn't any), but the user can ask for more data const fromDateResult = syncFromDate.determineFromDate({ @@ -965,7 +1069,7 @@ module.exports = function (Sync) { const password = getSyncEncryptPassword(null, upstreamServerEntry.credentials, upstreamServerEntry.autoEncrypt); app.logger.debug(`Sync ${syncLogEntry.id}: Exporting DB.`); - return new Promise(function (resolve, reject) { + return setSyncStep('EXPORT').then(() => new Promise(function (resolve, reject) { Sync.exportDatabase( filter, collections, @@ -980,6 +1084,7 @@ module.exports = function (Sync) { // nothing changed since the last sync, so the upstream server is already up to date if (err.code === 'NO-DATA') { app.logger.debug(`Sync ${syncLogEntry.id}: No data to sync.`); + syncLogEntry.syncStep = 'NO_DATA'; return resolve(); } @@ -988,7 +1093,7 @@ module.exports = function (Sync) { app.logger.debug(`Sync ${syncLogEntry.id}: DB exported at ${fileName}.`); return resolve(fileName); }); - }); + })); }) .then(function (exportedDBFileName) { // nothing to send @@ -997,7 +1102,7 @@ module.exports = function (Sync) { } // 2: send DB to be synced on the upstream server - return Sync.sendDBSnapshotForImport(upstreamServerEntry, exportedDBFileName, true, syncLogEntry); + return setSyncStep('SEND').then(() => Sync.sendDBSnapshotForImport(upstreamServerEntry, exportedDBFileName, true, syncLogEntry)); }) .then(function () { // sync was successful diff --git a/common/models/sync-log.json b/common/models/sync-log.json index 529860b9..62482319 100644 --- a/common/models/sync-log.json +++ b/common/models/sync-log.json @@ -23,6 +23,18 @@ "syncClientId": { "type": "string", "description": "Client ID which is synced with the server. Present in the entries for Server instances" + }, + "syncStep": { + "type": "string", + "description": "Step of a sync with an upstream server: CONNECT, CREDENTIALS, EXPORT, SEND, IMPORT or NO_DATA (nothing to send). When the sync fails, it is the step that failed" + }, + "syncStepErrorCode": { + "type": "string", + "description": "Why the server couldn't be reached or didn't accept the credentials (e.g. CONNECTION_REFUSED, INVALID_CREDENTIALS)" + }, + "syncStepErrorDetail": { + "type": "string", + "description": "Technical code of the connection error" } }, "validations": [], diff --git a/common/models/sync.js b/common/models/sync.js index 64cc2457..0d5cee7e 100644 --- a/common/models/sync.js +++ b/common/models/sync.js @@ -611,6 +611,15 @@ module.exports = function (Sync) { return client.sendDBSnapshotForImport(DBSnapshotFileName, asynchronous, upstreamServer.autoEncrypt ? 'true' : 'false') .then(function (syncLogId) { app.logger.debug(`Sync ${syncLogEntry.id}: Upstream server import: received upstream server sync log id: ${syncLogId}`); + + // the package was received, now the upstream server is importing it + syncLogEntry.syncStep = 'IMPORT'; + syncLogEntry + .save() + .catch((err) => { + app.logger.debug(`Sync ${syncLogEntry.id}: Error updating sync log entry step. ${err}`); + }); + // initialize container for server sync log entry status check connection error // will be updated only with connection errors let statusCheckConnectionError = null; diff --git a/server/boot/scheduler.json b/server/boot/scheduler.json index ad8b1328..07bc0dfa 100644 --- a/server/boot/scheduler.json +++ b/server/boot/scheduler.json @@ -1 +1 @@ -{"sync":{},"backup":{"startTime":"2026-06-17T14:01:55.370Z","lastExecutedTime":"2026-09-29T01:20:36.360Z","timeUnit":"h","interval":24,"backupTime":null},"backupCleanup":{"startTime":"2026-06-17T14:01:55.370Z","lastExecutedTime":"2026-09-29T01:20:36.359Z","timeUnit":"d","interval":90},"teamNotifications":{"startTime":"2026-06-17T14:01:55.370Z","lastExecutedTime":null,"timeUnit":"m","interval":5}} \ No newline at end of file +{"sync":{"http://localhost:3001/api":{"startTime":"2026-09-30T01:57:43.683Z","lastExecutedTime":"2026-10-05T01:48:48.141Z","timeUnit":"h","interval":1}},"backup":{"startTime":"2026-06-17T14:01:55.370Z","lastExecutedTime":"2026-09-29T01:20:36.360Z","timeUnit":"h","interval":24,"backupTime":null},"backupCleanup":{"startTime":"2026-06-17T14:01:55.370Z","lastExecutedTime":"2026-09-29T01:20:36.359Z","timeUnit":"d","interval":90},"teamNotifications":{"startTime":"2026-06-17T14:01:55.370Z","lastExecutedTime":"2026-10-05T01:55:02.753Z","timeUnit":"m","interval":5}} \ No newline at end of file diff --git a/server/config.json b/server/config.json index efbc2684..1e90de9a 100644 --- a/server/config.json +++ b/server/config.json @@ -108,7 +108,14 @@ "signoutUsersOnRestart": false, "cors": { "enabled": false, - "whitelist": [] + "whitelist": [ + "http://54.232.189.113:3000", + "https://54.232.189.113:3000", + "http://10.0.8.2:3000", + "https://10.0.8.2:3000", + "http://192.168.1.22:3000", + "https://192.168.1.22:3000" + ] }, "enableConfigRewrite": true, "authToken": { diff --git a/server/install/scripts/migrateModelData.js b/server/install/scripts/migrateModelData.js index d87ed3d1..7d82ec23 100644 --- a/server/install/scripts/migrateModelData.js +++ b/server/install/scripts/migrateModelData.js @@ -701,13 +701,13 @@ const migrationVersions = [{ fileName: 'languageToken.js', actions: [{ name: 'createUpdateLanguageTokens', - buildNo: 1 + buildNo: 6 }, { name: 'createUpdateSingleEnglishLanguageTokens', buildNo: 1 }, { name: 'createUpdatePortugueseLanguageTokens', - buildNo: 1 + buildNo: 6 }] }] }]; diff --git a/server/install/scripts/migrations/2.58.2/data/english/english_us.json b/server/install/scripts/migrations/2.58.2/data/english/english_us.json index 838f7c08..ecfd961d 100644 --- a/server/install/scripts/migrations/2.58.2/data/english/english_us.json +++ b/server/install/scripts/migrations/2.58.2/data/english/english_us.json @@ -372,6 +372,120 @@ }, "LNG_SYNC_DIRECTION_RECEIVED": { "translation": "Received" + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_TITLE": { + "translation": "Synchronize with server" + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_SINCE_LAST": { + "translation": "Only the records created or updated since the last successful synchronization are sent. This is the usual option and the fastest one." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_FROM_DATE": { + "translation": "The records created or updated since the chosen date are sent. Use it to send older data again, for example to fill a server that was recreated." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_ALL": { + "translation": "All the data is sent. It may take a long time; the server only updates the records that are newer than the ones it already has." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_FIRST_SYNC": { + "translation": "There was no synchronization with this server yet, so all the data will be sent." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_OUTBREAKS": { + "translation": "Outbreaks" + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_OUTBREAKS_TOOLTIP": { + "translation": "Filled with the outbreaks configured for the server. Leave it empty to send all the outbreaks." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_PARTIAL": { + "translation": "The chosen outbreaks don't include all the ones configured for this server. This synchronization isn't used to determine what the next ones send, so they still send what changed in the outbreaks that were left out." + }, + "LNG_UPSTREAM_SERVER_FIELD_LABEL_OUTBREAKS_TO_SYNC": { + "translation": "Outbreaks to synchronize" + }, + "LNG_UPSTREAM_SERVER_FIELD_LABEL_OUTBREAKS_TO_SYNC_ALL": { + "translation": "All outbreaks" + }, + "LNG_UPSTREAM_SERVER_FIELD_LABEL_OUTBREAKS_TO_SYNC_DESCRIPTION": { + "translation": "Leave it empty to synchronize all the outbreaks. The scheduled synchronizations and the ones made on every change send only the chosen outbreaks. When an outbreak is added, the next synchronization sends all the data of the chosen outbreaks." + }, + "LNG_SYNC_PROGRESS_DIALOG_TITLE": { + "translation": "Synchronizing with the server" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP": { + "translation": "Step" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP_CONNECT": { + "translation": "Connecting servers" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP_CREDENTIALS": { + "translation": "Validating credentials" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP_SYNC": { + "translation": "Synchronizing data" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP_FINAL": { + "translation": "Final status" + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_CONNECT": { + "translation": "Establishing the connection with the server..." + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_CREDENTIALS": { + "translation": "Checking the client ID and secret..." + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_EXPORT": { + "translation": "Preparing the data to send..." + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_SEND": { + "translation": "Sending the data to the server..." + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_IMPORT": { + "translation": "The server is importing the data..." + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_SUCCESS": { + "translation": "Success!" + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_SUCCESS_MESSAGE": { + "translation": "The data was synchronized." + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_WARNING": { + "translation": "Synchronized with warnings" + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_WARNING_MESSAGE": { + "translation": "The data was synchronized, but some records had problems. See the details in Track synchronization." + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_UP_TO_DATE": { + "translation": "Up to date" + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_UP_TO_DATE_MESSAGE": { + "translation": "There was nothing new to send since the last synchronization." + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_ERROR": { + "translation": "Synchronization failed" + }, + "LNG_SYNC_PROGRESS_DIALOG_ERROR_CONNECT": { + "translation": "It wasn't possible to connect to the server \"{{name}}\". Check if it is online and if the URL is correct." + }, + "LNG_SYNC_PROGRESS_DIALOG_ERROR_CREDENTIALS": { + "translation": "The server \"{{name}}\" didn't accept the credentials. Check the client ID and secret in the server settings." + }, + "LNG_SYNC_PROGRESS_DIALOG_ERROR_SYNC": { + "translation": "An error occurred while synchronizing the data." + }, + "LNG_SYNC_PROGRESS_DIALOG_LOG_TITLE": { + "translation": "Error details" + }, + "LNG_SYNC_PROGRESS_DIALOG_LOG_HELP": { + "translation": "If the problem continues, contact the system administrator or the person responsible for the destination server." + }, + "LNG_SYNC_PROGRESS_DIALOG_VIEW_ERROR_BUTTON": { + "translation": "View full log" + }, + "LNG_SYNC_PROGRESS_DIALOG_CLOSE_BUTTON": { + "translation": "Close" + }, + "LNG_SYNC_PROGRESS_DIALOG_BACKGROUND_BUTTON": { + "translation": "Continue in background" + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_OUTBREAKS_ALL": { + "translation": "All outbreaks (none selected)" } } } diff --git a/server/install/scripts/migrations/2.58.2/data/portuguese/portuguese.json b/server/install/scripts/migrations/2.58.2/data/portuguese/portuguese.json index bb674e07..e65011bd 100644 --- a/server/install/scripts/migrations/2.58.2/data/portuguese/portuguese.json +++ b/server/install/scripts/migrations/2.58.2/data/portuguese/portuguese.json @@ -411,6 +411,120 @@ }, "LNG_SYNC_DIRECTION_RECEIVED": { "translation": "Recebido" + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_TITLE": { + "translation": "Sincronizar com o servidor" + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_SINCE_LAST": { + "translation": "São enviados apenas os registros criados ou alterados desde a última sincronização com sucesso. É a opção usual e a mais rápida." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_FROM_DATE": { + "translation": "São enviados os registros criados ou alterados desde a data escolhida. Use para reenviar dados mais antigos, por exemplo para preencher um servidor que foi recriado." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_ALL": { + "translation": "Todos os dados são enviados. Pode demorar bastante; o servidor só atualiza os registros mais novos que os que ele já tem." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_FIRST_SYNC": { + "translation": "Ainda não houve sincronização com este servidor, então todos os dados serão enviados." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_OUTBREAKS": { + "translation": "Surtos" + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_OUTBREAKS_TOOLTIP": { + "translation": "Vem preenchido com os surtos configurados no servidor. Deixe vazio para enviar todos os surtos." + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_HINT_PARTIAL": { + "translation": "Os surtos escolhidos não incluem todos os configurados neste servidor. Esta sincronização não é usada para definir o que as próximas enviam, então elas continuam enviando o que mudou nos surtos que ficaram de fora." + }, + "LNG_UPSTREAM_SERVER_FIELD_LABEL_OUTBREAKS_TO_SYNC": { + "translation": "Surtos a sincronizar" + }, + "LNG_UPSTREAM_SERVER_FIELD_LABEL_OUTBREAKS_TO_SYNC_ALL": { + "translation": "Todos os surtos" + }, + "LNG_UPSTREAM_SERVER_FIELD_LABEL_OUTBREAKS_TO_SYNC_DESCRIPTION": { + "translation": "Deixe vazio para sincronizar todos os surtos. As sincronizações agendadas e as feitas a cada alteração enviam apenas os surtos escolhidos. Ao incluir um surto, a próxima sincronização envia todos os dados dos surtos escolhidos." + }, + "LNG_SYNC_PROGRESS_DIALOG_TITLE": { + "translation": "Sincronizando com o servidor" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP": { + "translation": "Etapa" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP_CONNECT": { + "translation": "Conectando servidores" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP_CREDENTIALS": { + "translation": "Validando credenciais" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP_SYNC": { + "translation": "Sincronizando dados" + }, + "LNG_SYNC_PROGRESS_DIALOG_STEP_FINAL": { + "translation": "Status final" + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_CONNECT": { + "translation": "Estabelecendo a conexão com o servidor..." + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_CREDENTIALS": { + "translation": "Verificando o ID e o segredo do cliente..." + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_EXPORT": { + "translation": "Preparando os dados para envio..." + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_SEND": { + "translation": "Enviando os dados para o servidor..." + }, + "LNG_SYNC_PROGRESS_DIALOG_PHASE_IMPORT": { + "translation": "O servidor está importando os dados..." + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_SUCCESS": { + "translation": "Sucesso!" + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_SUCCESS_MESSAGE": { + "translation": "Os dados foram sincronizados." + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_WARNING": { + "translation": "Sincronizado com avisos" + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_WARNING_MESSAGE": { + "translation": "Os dados foram sincronizados, mas alguns registros tiveram problemas. Veja os detalhes em Acompanhar sincronização." + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_UP_TO_DATE": { + "translation": "Tudo em dia" + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_UP_TO_DATE_MESSAGE": { + "translation": "Não havia nada novo para enviar desde a última sincronização." + }, + "LNG_SYNC_PROGRESS_DIALOG_RESULT_ERROR": { + "translation": "Falha na sincronização" + }, + "LNG_SYNC_PROGRESS_DIALOG_ERROR_CONNECT": { + "translation": "Não foi possível conectar ao servidor \"{{name}}\". Verifique se ele está online e se a URL está correta." + }, + "LNG_SYNC_PROGRESS_DIALOG_ERROR_CREDENTIALS": { + "translation": "O servidor \"{{name}}\" não aceitou as credenciais. Verifique o ID e o segredo do cliente no cadastro do servidor." + }, + "LNG_SYNC_PROGRESS_DIALOG_ERROR_SYNC": { + "translation": "Ocorreu um erro ao sincronizar os dados." + }, + "LNG_SYNC_PROGRESS_DIALOG_LOG_TITLE": { + "translation": "Detalhes do erro" + }, + "LNG_SYNC_PROGRESS_DIALOG_LOG_HELP": { + "translation": "Se o problema continuar, entre em contato com o administrador do sistema ou com o responsável pelo servidor de destino." + }, + "LNG_SYNC_PROGRESS_DIALOG_VIEW_ERROR_BUTTON": { + "translation": "Ver log completo" + }, + "LNG_SYNC_PROGRESS_DIALOG_CLOSE_BUTTON": { + "translation": "Fechar" + }, + "LNG_SYNC_PROGRESS_DIALOG_BACKGROUND_BUTTON": { + "translation": "Continuar em segundo plano" + }, + "LNG_PAGE_LIST_SYSTEM_UPSTREAM_SERVERS_SYNC_DIALOG_OUTBREAKS_ALL": { + "translation": "Todos os surtos (nenhum selecionado)" } } } diff --git a/server/models/sync-parameters.json b/server/models/sync-parameters.json index c1c61bb5..802aaed0 100644 --- a/server/models/sync-parameters.json +++ b/server/models/sync-parameters.json @@ -15,11 +15,17 @@ }, "fromDate": { "type": "date", - "description": "Send the data that was updated since this date. It only has effect if it is older than the start of the last successful sync with the server (or when there wasn't any), since the data updated between them would never be sent" + "description": "Send the data that was updated since this date. It only has effect if it is older than the start of the last successful sync with the server, since the data updated between them would never be sent. When there wasn't any successful sync, all the data is sent" }, "fullSync": { "type": "boolean", "description": "Send all the data, no matter when the last successful sync with the server was made. When true, fromDate is ignored" + }, + "outbreakIDs": { + "type": [ + "string" + ], + "description": "Send only the data of these outbreaks (limited to the ones accepted by the upstream server). When not sent, the outbreaks configured for the upstream server are used. Empty means all the outbreaks" } }, "validations": [], diff --git a/server/models/upstream-server.json b/server/models/upstream-server.json index f1199c8f..ac94e947 100644 --- a/server/models/upstream-server.json +++ b/server/models/upstream-server.json @@ -47,6 +47,12 @@ "type": "boolean", "default": true, "description": "Auto Encrypt; Default 'true'" + }, + "outbreakIDs": { + "type": [ + "string" + ], + "description": "Outbreaks that are synced with this server. Empty means all the outbreaks" } }, "validations": [],