@crowdin/app-project-module 0.38.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/handlers/integration/crowdin-file-progress.js +15 -3
- package/out/handlers/integration/crowdin-files.js +17 -5
- package/out/handlers/integration/crowdin-update.js +15 -3
- package/out/handlers/integration/integration-data.js +19 -7
- package/out/handlers/integration/integration-login.js +14 -2
- package/out/handlers/integration/integration-logout.js +14 -2
- package/out/handlers/integration/integration-update.js +15 -3
- package/out/handlers/user-errors.d.ts +3 -0
- package/out/handlers/user-errors.js +40 -0
- package/out/index.js +2 -0
- package/out/models/index.d.ts +9 -0
- package/out/static/css/styles.css +49 -1
- package/out/storage/index.d.ts +4 -1
- package/out/storage/mysql.d.ts +4 -1
- package/out/storage/mysql.js +49 -0
- package/out/storage/postgre.d.ts +4 -1
- package/out/storage/postgre.js +49 -0
- package/out/storage/sqlite.d.ts +4 -1
- package/out/storage/sqlite.js +37 -0
- package/out/util/logger.d.ts +7 -0
- package/out/util/logger.js +90 -4
- package/out/views/main.handlebars +188 -81
- package/package.json +2 -2
|
@@ -10,14 +10,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const util_1 = require("../../util");
|
|
13
|
+
const logger_1 = require("../../util/logger");
|
|
13
14
|
function handle(integration) {
|
|
14
15
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
15
16
|
const fileId = Number(req.params.fileId || req.body.fileId);
|
|
16
17
|
req.logInfo(`Loading translation progress for file ${fileId}`);
|
|
17
18
|
if (integration.getFileProgress) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
try {
|
|
20
|
+
const progress = yield integration.getFileProgress(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, fileId);
|
|
21
|
+
req.logInfo(`Translation progress for file ${fileId} ${JSON.stringify(progress, null, 2)}`);
|
|
22
|
+
res.send(progress);
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
yield (0, logger_1.handleUserError)({
|
|
26
|
+
action: 'Get Crowdin files progress',
|
|
27
|
+
error: e,
|
|
28
|
+
crowdinId: req.crowdinContext.crowdinId,
|
|
29
|
+
clientId: req.crowdinContext.clientId,
|
|
30
|
+
});
|
|
31
|
+
throw e;
|
|
32
|
+
}
|
|
21
33
|
}
|
|
22
34
|
else {
|
|
23
35
|
res.send({});
|
|
@@ -11,17 +11,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const util_1 = require("../../util");
|
|
13
13
|
const defaults_1 = require("../../util/defaults");
|
|
14
|
+
const logger_1 = require("../../util/logger");
|
|
14
15
|
function handle(config, integration) {
|
|
15
16
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
16
17
|
req.logInfo('Loading crowdin files');
|
|
17
18
|
if (integration.getCrowdinFiles) {
|
|
18
19
|
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, req.crowdinApiClient, req.crowdinContext.jwtPayload.context.project_id);
|
|
19
20
|
req.logInfo(`Loading files ${rootFolder ? `from folder ${rootFolder.id}` : 'from root'}`);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
try {
|
|
22
|
+
const files = integration.getCrowdinFiles
|
|
23
|
+
? yield integration.getCrowdinFiles(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, rootFolder, req.integrationSettings)
|
|
24
|
+
: [];
|
|
25
|
+
req.logInfo(`Returning ${files.length} files`);
|
|
26
|
+
res.send(files);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
yield (0, logger_1.handleUserError)({
|
|
30
|
+
action: 'Get Crowdin files',
|
|
31
|
+
error: e,
|
|
32
|
+
crowdinId: req.crowdinContext.crowdinId,
|
|
33
|
+
clientId: req.crowdinContext.clientId,
|
|
34
|
+
});
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
25
37
|
}
|
|
26
38
|
else {
|
|
27
39
|
res.send([]);
|
|
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const util_1 = require("../../util");
|
|
13
13
|
const defaults_1 = require("../../util/defaults");
|
|
14
|
+
const logger_1 = require("../../util/logger");
|
|
14
15
|
function handle(config, integration) {
|
|
15
16
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
16
17
|
var _a, _b;
|
|
@@ -25,10 +26,21 @@ function handle(config, integration) {
|
|
|
25
26
|
if (((_b = config.api) === null || _b === void 0 ? void 0 : _b.default) && req.body.files) {
|
|
26
27
|
req.body = req.body.files;
|
|
27
28
|
}
|
|
28
|
-
const result = yield integration.updateCrowdin(projectId, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings, uploadTranslations);
|
|
29
29
|
let message;
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
try {
|
|
31
|
+
const result = yield integration.updateCrowdin(projectId, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings, uploadTranslations);
|
|
32
|
+
if ((0, util_1.isExtendedResultType)(result)) {
|
|
33
|
+
message = result.message;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
yield (0, logger_1.handleUserError)({
|
|
38
|
+
action: 'Sync files to Crowdin',
|
|
39
|
+
error: e,
|
|
40
|
+
crowdinId: req.crowdinContext.crowdinId,
|
|
41
|
+
clientId: req.crowdinContext.clientId,
|
|
42
|
+
});
|
|
43
|
+
throw e;
|
|
32
44
|
}
|
|
33
45
|
res.send({ message });
|
|
34
46
|
}));
|
|
@@ -10,21 +10,33 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const util_1 = require("../../util");
|
|
13
|
+
const logger_1 = require("../../util/logger");
|
|
13
14
|
function handle(integration) {
|
|
14
15
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
15
16
|
const { parent_id: parentId, search, page } = req.query;
|
|
16
17
|
req.logInfo('Recieved request to get integration data');
|
|
17
|
-
const result = yield integration.getIntegrationFiles(req.integrationCredentials, req.integrationSettings, parentId, search, page);
|
|
18
18
|
let message;
|
|
19
19
|
let stopPagination;
|
|
20
20
|
let files;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
try {
|
|
22
|
+
const result = yield integration.getIntegrationFiles(req.integrationCredentials, req.integrationSettings, parentId, search, page);
|
|
23
|
+
if ((0, util_1.isExtendedResultType)(result)) {
|
|
24
|
+
files = result.data;
|
|
25
|
+
message = result.message;
|
|
26
|
+
stopPagination = result.stopPagination;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
files = result;
|
|
30
|
+
}
|
|
25
31
|
}
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
catch (e) {
|
|
33
|
+
yield (0, logger_1.handleUserError)({
|
|
34
|
+
action: 'Get External Service data',
|
|
35
|
+
error: e,
|
|
36
|
+
crowdinId: req.crowdinContext.crowdinId,
|
|
37
|
+
clientId: req.crowdinContext.clientId,
|
|
38
|
+
});
|
|
39
|
+
throw e;
|
|
28
40
|
}
|
|
29
41
|
req.logInfo(`Integration data response ${JSON.stringify(files, null, 2)}`);
|
|
30
42
|
res.send({ data: files, message, stopPagination });
|
|
@@ -11,12 +11,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const storage_1 = require("../../storage");
|
|
13
13
|
const util_1 = require("../../util");
|
|
14
|
+
const logger_1 = require("../../util/logger");
|
|
14
15
|
function handle(config, integration) {
|
|
15
16
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
16
17
|
req.logInfo('Received integration login request');
|
|
17
18
|
if (integration.checkConnection) {
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
try {
|
|
20
|
+
req.logInfo('Checking the integration credentials');
|
|
21
|
+
yield integration.checkConnection(req.body.credentials);
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
yield (0, logger_1.handleUserError)({
|
|
25
|
+
action: 'External Service login',
|
|
26
|
+
error: e,
|
|
27
|
+
crowdinId: req.crowdinContext.crowdinId,
|
|
28
|
+
clientId: req.crowdinContext.clientId,
|
|
29
|
+
});
|
|
30
|
+
throw e;
|
|
31
|
+
}
|
|
20
32
|
}
|
|
21
33
|
const existing = yield (0, storage_1.getStorage)().getIntegrationCredentials(req.crowdinContext.clientId);
|
|
22
34
|
if (!!existing) {
|
|
@@ -13,12 +13,24 @@ const storage_1 = require("../../storage");
|
|
|
13
13
|
const util_1 = require("../../util");
|
|
14
14
|
const connection_1 = require("../../util/connection");
|
|
15
15
|
const webhooks_1 = require("../../util/webhooks");
|
|
16
|
+
const logger_1 = require("../../util/logger");
|
|
16
17
|
function handle(config, integration) {
|
|
17
18
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
18
19
|
req.logInfo('Received integration logout request');
|
|
19
20
|
if (integration.onLogout) {
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
try {
|
|
22
|
+
req.logInfo('Invoking onLogout hook');
|
|
23
|
+
yield integration.onLogout(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.integrationSettings);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
yield (0, logger_1.handleUserError)({
|
|
27
|
+
action: 'External Service logout',
|
|
28
|
+
error: e,
|
|
29
|
+
crowdinId: req.crowdinContext.crowdinId,
|
|
30
|
+
clientId: req.crowdinContext.clientId,
|
|
31
|
+
});
|
|
32
|
+
throw e;
|
|
33
|
+
}
|
|
22
34
|
}
|
|
23
35
|
req.logInfo(`Deleting integration credentials for ${req.crowdinContext.clientId} client`);
|
|
24
36
|
yield (0, storage_1.getStorage)().deleteIntegrationCredentials(req.crowdinContext.clientId);
|
|
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const util_1 = require("../../util");
|
|
13
13
|
const defaults_1 = require("../../util/defaults");
|
|
14
|
+
const logger_1 = require("../../util/logger");
|
|
14
15
|
function handle(config, integration) {
|
|
15
16
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
16
17
|
var _a;
|
|
@@ -23,10 +24,21 @@ function handle(config, integration) {
|
|
|
23
24
|
if (((_a = config.api) === null || _a === void 0 ? void 0 : _a.default) && req.body.files) {
|
|
24
25
|
req.body = req.body.files;
|
|
25
26
|
}
|
|
26
|
-
const result = yield integration.updateIntegration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings);
|
|
27
27
|
let message;
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
try {
|
|
29
|
+
const result = yield integration.updateIntegration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings);
|
|
30
|
+
if ((0, util_1.isExtendedResultType)(result)) {
|
|
31
|
+
message = result.message;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
yield (0, logger_1.handleUserError)({
|
|
36
|
+
action: 'Sync files to External Service',
|
|
37
|
+
error: e,
|
|
38
|
+
crowdinId: req.crowdinContext.crowdinId,
|
|
39
|
+
clientId: req.crowdinContext.clientId,
|
|
40
|
+
});
|
|
41
|
+
throw e;
|
|
30
42
|
}
|
|
31
43
|
res.send({ message });
|
|
32
44
|
}));
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/// <reference types="qs" />
|
|
2
|
+
import { Response } from 'express';
|
|
3
|
+
export default function handle(): (req: import("../models").CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const storage_1 = require("../storage");
|
|
13
|
+
const util_1 = require("../util");
|
|
14
|
+
function handle() {
|
|
15
|
+
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
16
|
+
var _a;
|
|
17
|
+
let userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
18
|
+
if (req.crowdinContext.jwtPayload.context.user_id) {
|
|
19
|
+
userTimezone = (yield req.crowdinApiClient.usersApi.getUserInfo(req.crowdinContext.jwtPayload.context.user_id)).data.timezone;
|
|
20
|
+
}
|
|
21
|
+
req.logInfo(`Loading user errors for crowdinId ${req.crowdinContext.crowdinId} and integrationId ${req.crowdinContext.clientId}`);
|
|
22
|
+
const userErrors = (_a = (yield (0, storage_1.getStorage)().getAllUserErrors(req.crowdinContext.crowdinId, req.crowdinContext.clientId))) === null || _a === void 0 ? void 0 : _a.map((userError) => {
|
|
23
|
+
const date = new Date(+userError.createdAt);
|
|
24
|
+
// Format the date as 'MMM DD, YYYY HH:mm'
|
|
25
|
+
const formattedDate = new Intl.DateTimeFormat('en-US', {
|
|
26
|
+
year: 'numeric',
|
|
27
|
+
month: 'short',
|
|
28
|
+
day: 'numeric',
|
|
29
|
+
hour: '2-digit',
|
|
30
|
+
minute: '2-digit',
|
|
31
|
+
hour12: false,
|
|
32
|
+
timeZone: userTimezone,
|
|
33
|
+
}).format(date);
|
|
34
|
+
return Object.assign(Object.assign({}, userError), { createdAt: formattedDate });
|
|
35
|
+
});
|
|
36
|
+
req.logInfo(`Returning ${userErrors === null || userErrors === void 0 ? void 0 : userErrors.length} user errors`);
|
|
37
|
+
res.send(userErrors);
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
exports.default = handle;
|
package/out/index.js
CHANGED
|
@@ -67,6 +67,7 @@ const sync_settings_save_1 = __importDefault(require("./handlers/integration/syn
|
|
|
67
67
|
const manifest_1 = __importDefault(require("./handlers/manifest"));
|
|
68
68
|
const subscription_paid_1 = __importDefault(require("./handlers/subscription-paid"));
|
|
69
69
|
const uninstall_1 = __importDefault(require("./handlers/uninstall"));
|
|
70
|
+
const user_errors_1 = __importDefault(require("./handlers/user-errors"));
|
|
70
71
|
const crowdin_client_1 = __importStar(require("./middlewares/crowdin-client"));
|
|
71
72
|
const integration_credentials_1 = __importDefault(require("./middlewares/integration-credentials"));
|
|
72
73
|
const json_response_1 = __importDefault(require("./middlewares/json-response"));
|
|
@@ -184,6 +185,7 @@ function addCrowdinEndpoints(app, clientConfig) {
|
|
|
184
185
|
(0, webhooks_1.listenQueueMessage)(config, integrationLogic, integrationLogic.webhooks.queueUrl, config.identifier);
|
|
185
186
|
}
|
|
186
187
|
}
|
|
188
|
+
app.get('/api/user-errors', json_response_1.default, (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, user_errors_1.default)());
|
|
187
189
|
}
|
|
188
190
|
if (config.customFileFormat) {
|
|
189
191
|
(0, defaults_1.applyFileProcessorsModuleDefaults)(config, config.customFileFormat);
|
package/out/models/index.d.ts
CHANGED
|
@@ -860,6 +860,15 @@ export interface IntegrationWebhooks {
|
|
|
860
860
|
crowdinId: string;
|
|
861
861
|
provider: Provider;
|
|
862
862
|
}
|
|
863
|
+
export interface UserErrors {
|
|
864
|
+
id: number;
|
|
865
|
+
action: string;
|
|
866
|
+
message: string;
|
|
867
|
+
data: any;
|
|
868
|
+
createdAt: string;
|
|
869
|
+
crowdinId: string;
|
|
870
|
+
integrationId?: string;
|
|
871
|
+
}
|
|
863
872
|
export interface ImagePath {
|
|
864
873
|
/**
|
|
865
874
|
* path to app logo (e.g. {@example join(__dirname, 'logo.png')})
|
|
@@ -161,4 +161,52 @@
|
|
|
161
161
|
|
|
162
162
|
#form .MuiButtonBase-root[type="submit"]:hover {
|
|
163
163
|
background: rgba(236, 239, 241, .54);
|
|
164
|
-
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#buttons {
|
|
167
|
+
display: flex;
|
|
168
|
+
justify-content: space-between;
|
|
169
|
+
width: 100%;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
#buttons crowdin-button:nth-of-type(2) {
|
|
173
|
+
margin-right: auto;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#user-errors-table {
|
|
177
|
+
margin-top: 24px;
|
|
178
|
+
display: block;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.error-detail-table {
|
|
182
|
+
height: auto;
|
|
183
|
+
max-height: 579px;
|
|
184
|
+
margin: -24px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.error-detail-table table {
|
|
188
|
+
border-spacing: unset;
|
|
189
|
+
width: 100%;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.error-detail-table tr:first-child td {
|
|
193
|
+
padding-top: 24px;
|
|
194
|
+
border-top-right-radius: 12px;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.error-detail-table tr td:first-child {
|
|
198
|
+
color: #263238;
|
|
199
|
+
font-weight: 500;
|
|
200
|
+
min-width: 200px;
|
|
201
|
+
vertical-align: top;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.error-detail-table tr td:last-child {
|
|
205
|
+
background: #f6f6f6;
|
|
206
|
+
color: rgba(38,50,56,.87);
|
|
207
|
+
word-break: break-all;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.error-detail-table tr td {
|
|
211
|
+
padding: 8px 24px;
|
|
212
|
+
}
|
package/out/storage/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Config, CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, Provider } from '../models';
|
|
1
|
+
import { Config, CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, Provider, UserErrors } from '../models';
|
|
2
2
|
export interface Storage {
|
|
3
3
|
migrate(): Promise<void>;
|
|
4
4
|
saveCrowdinCredentials(credentials: CrowdinCredentials): Promise<void>;
|
|
@@ -29,6 +29,9 @@ export interface Storage {
|
|
|
29
29
|
getWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: Provider): Promise<IntegrationWebhooks | undefined>;
|
|
30
30
|
saveWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: Provider): Promise<void>;
|
|
31
31
|
deleteWebhooks(fileIds: any[], integrationId: string, crowdinId: string, provider: Provider): Promise<void>;
|
|
32
|
+
getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[] | undefined>;
|
|
33
|
+
saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
|
|
34
|
+
deleteUserErrors(date: string, crowdinId: string, integrationId?: string): Promise<void>;
|
|
32
35
|
}
|
|
33
36
|
export declare function initialize(config: Config): Promise<void>;
|
|
34
37
|
export declare function getStorage(): Storage;
|
package/out/storage/mysql.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Storage } from '.';
|
|
2
|
-
import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks } from '../models';
|
|
2
|
+
import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, UserErrors } from '../models';
|
|
3
3
|
export interface MySQLStorageConfig {
|
|
4
4
|
uri?: string;
|
|
5
5
|
host?: string;
|
|
@@ -46,4 +46,7 @@ export declare class MySQLStorage implements Storage {
|
|
|
46
46
|
getWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<IntegrationWebhooks | undefined>;
|
|
47
47
|
saveWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<void>;
|
|
48
48
|
deleteWebhooks(fileIds: any[], integrationId: string, crowdinId: string, provider: string): Promise<void>;
|
|
49
|
+
getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[] | undefined>;
|
|
50
|
+
saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
|
|
51
|
+
deleteUserErrors(createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
|
|
49
52
|
}
|
package/out/storage/mysql.js
CHANGED
|
@@ -124,6 +124,18 @@ class MySQLStorage {
|
|
|
124
124
|
provider varchar(255) not null
|
|
125
125
|
)
|
|
126
126
|
`);
|
|
127
|
+
yield connection.execute(`
|
|
128
|
+
create table if not exists user_errors
|
|
129
|
+
(
|
|
130
|
+
id int auto_increment primary key,
|
|
131
|
+
action varchar(255) not null,
|
|
132
|
+
message varchar(255) not null,
|
|
133
|
+
data text,
|
|
134
|
+
created_at varchar(255) not null,
|
|
135
|
+
crowdin_id varchar(255) not null,
|
|
136
|
+
integration_id varchar(255)
|
|
137
|
+
)
|
|
138
|
+
`);
|
|
127
139
|
});
|
|
128
140
|
}
|
|
129
141
|
saveCrowdinCredentials(credentials) {
|
|
@@ -187,6 +199,7 @@ class MySQLStorage {
|
|
|
187
199
|
yield connection.execute('DELETE FROM app_metadata WHERE crowdin_id = ?', [id]);
|
|
188
200
|
yield connection.execute('DELETE FROM files_snapshot WHERE crowdin_id = ?', [id]);
|
|
189
201
|
yield connection.execute('DELETE FROM webhooks WHERE crowdin_id = ?', [id]);
|
|
202
|
+
yield connection.execute('DELETE FROM user_errors WHERE crowdin_id = ?', [id]);
|
|
190
203
|
}));
|
|
191
204
|
});
|
|
192
205
|
}
|
|
@@ -249,6 +262,7 @@ class MySQLStorage {
|
|
|
249
262
|
yield connection.execute('DELETE FROM sync_settings where crowdin_id = ?', [crowdinId]);
|
|
250
263
|
yield connection.execute('DELETE FROM files_snapshot where crowdin_id = ?', [crowdinId]);
|
|
251
264
|
yield connection.execute('DELETE FROM webhooks where crowdin_id = ?', [crowdinId]);
|
|
265
|
+
yield connection.execute('DELETE FROM user_errors where crowdin_id = ?', [crowdinId]);
|
|
252
266
|
}));
|
|
253
267
|
});
|
|
254
268
|
}
|
|
@@ -380,5 +394,40 @@ class MySQLStorage {
|
|
|
380
394
|
yield this.executeQuery((connection) => connection.execute(`DELETE FROM webhooks WHERE file_id IN (${placeholders}) AND integration_id = ? AND crowdin_id = ? AND provider = ?`, [...fileIds, integrationId, crowdinId, provider]));
|
|
381
395
|
});
|
|
382
396
|
}
|
|
397
|
+
getAllUserErrors(crowdinId, integrationId) {
|
|
398
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
399
|
+
yield this.dbPromise;
|
|
400
|
+
return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
|
|
401
|
+
let whereIntegrationCondition = 'integration_id is NULL';
|
|
402
|
+
const params = [crowdinId];
|
|
403
|
+
if (integrationId) {
|
|
404
|
+
whereIntegrationCondition = 'integration_id = ?';
|
|
405
|
+
params.push(integrationId);
|
|
406
|
+
}
|
|
407
|
+
const [rows] = yield connection.execute(`SELECT id, action, message, data, created_at as "createdAt" FROM user_errors WHERE crowdin_id = ? AND ${whereIntegrationCondition}`, params);
|
|
408
|
+
return rows || [];
|
|
409
|
+
}));
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
saveUserError(action, message, data, createdAt, crowdinId, integrationId) {
|
|
413
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
414
|
+
yield this.dbPromise;
|
|
415
|
+
yield this.executeQuery((connection) => connection.execute('INSERT INTO user_errors(action, message, data, created_at, integration_id, crowdin_id) VALUES (?, ?, ?, ?, ?, ?)', [action, message, data, createdAt, integrationId, crowdinId]));
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
deleteUserErrors(createdAt, crowdinId, integrationId) {
|
|
419
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
420
|
+
yield this.dbPromise;
|
|
421
|
+
yield this.executeQuery((connection) => {
|
|
422
|
+
let whereIntegrationCondition = 'integration_id is NULL';
|
|
423
|
+
const params = [createdAt, crowdinId];
|
|
424
|
+
if (integrationId) {
|
|
425
|
+
whereIntegrationCondition = 'integration_id = ?';
|
|
426
|
+
params.push(integrationId);
|
|
427
|
+
}
|
|
428
|
+
return connection.execute(`DELETE FROM user_errors WHERE created_at < ? AND crowdin_id = ? AND ${whereIntegrationCondition}`, params);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
}
|
|
383
432
|
}
|
|
384
433
|
exports.MySQLStorage = MySQLStorage;
|
package/out/storage/postgre.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Client } from 'pg';
|
|
2
2
|
import { Storage } from '.';
|
|
3
|
-
import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks } from '../models';
|
|
3
|
+
import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, UserErrors } from '../models';
|
|
4
4
|
export interface PostgreStorageConfig {
|
|
5
5
|
host?: string;
|
|
6
6
|
connectionString?: string;
|
|
@@ -51,4 +51,7 @@ export declare class PostgreStorage implements Storage {
|
|
|
51
51
|
getWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<IntegrationWebhooks | undefined>;
|
|
52
52
|
saveWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<void>;
|
|
53
53
|
deleteWebhooks(fileIds: any[], integrationId: string, crowdinId: string, provider: string): Promise<void>;
|
|
54
|
+
getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[] | undefined>;
|
|
55
|
+
saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
|
|
56
|
+
deleteUserErrors(createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
|
|
54
57
|
}
|
package/out/storage/postgre.js
CHANGED
|
@@ -138,6 +138,18 @@ class PostgreStorage {
|
|
|
138
138
|
provider varchar not null
|
|
139
139
|
)
|
|
140
140
|
`);
|
|
141
|
+
yield client.query(`
|
|
142
|
+
create table if not exists user_errors
|
|
143
|
+
(
|
|
144
|
+
id serial primary key,
|
|
145
|
+
action varchar not null,
|
|
146
|
+
message varchar not null,
|
|
147
|
+
data varchar,
|
|
148
|
+
created_at varchar not null,
|
|
149
|
+
crowdin_id varchar not null,
|
|
150
|
+
integration_id varchar
|
|
151
|
+
)
|
|
152
|
+
`);
|
|
141
153
|
});
|
|
142
154
|
}
|
|
143
155
|
saveCrowdinCredentials(credentials) {
|
|
@@ -201,6 +213,7 @@ class PostgreStorage {
|
|
|
201
213
|
yield client.query('DELETE FROM files_snapshot WHERE crowdin_id = $1', [id]);
|
|
202
214
|
yield client.query('DELETE FROM app_metadata WHERE crowdin_id = $1', [id]);
|
|
203
215
|
yield client.query('DELETE FROM webhooks WHERE crowdin_id = $1', [id]);
|
|
216
|
+
yield client.query('DELETE FROM user_errors WHERE crowdin_id = $1', [id]);
|
|
204
217
|
}));
|
|
205
218
|
});
|
|
206
219
|
}
|
|
@@ -263,6 +276,7 @@ class PostgreStorage {
|
|
|
263
276
|
yield client.query('DELETE FROM sync_settings where crowdin_id = $1', [crowdinId]);
|
|
264
277
|
yield client.query('DELETE FROM files_snapshot where crowdin_id = $1', [crowdinId]);
|
|
265
278
|
yield client.query('DELETE FROM webhooks where crowdin_id = $1', [crowdinId]);
|
|
279
|
+
yield client.query('DELETE FROM user_errors where crowdin_id = $1', [crowdinId]);
|
|
266
280
|
}));
|
|
267
281
|
});
|
|
268
282
|
}
|
|
@@ -395,5 +409,40 @@ class PostgreStorage {
|
|
|
395
409
|
yield this.executeQuery((client) => client.query(`DELETE FROM webhooks WHERE file_id IN ${placeholders} AND integration_id = $${++index} AND crowdin_id = $${++index} AND provider = $${++index}`, [...fileIds, integrationId, crowdinId, provider]));
|
|
396
410
|
});
|
|
397
411
|
}
|
|
412
|
+
getAllUserErrors(crowdinId, integrationId) {
|
|
413
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
414
|
+
yield this.dbPromise;
|
|
415
|
+
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
|
|
416
|
+
let whereIntegrationCondition = 'integration_id is NULL';
|
|
417
|
+
const params = [crowdinId];
|
|
418
|
+
if (integrationId) {
|
|
419
|
+
whereIntegrationCondition = 'integration_id = $2';
|
|
420
|
+
params.push(integrationId);
|
|
421
|
+
}
|
|
422
|
+
const res = yield client.query(`SELECT id, action, message, data, created_at as "createdAt", integration_id as "integrationId", crowdin_id as "crowdinId" FROM user_errors WHERE crowdin_id = $1 AND ${whereIntegrationCondition}`, params);
|
|
423
|
+
return (res === null || res === void 0 ? void 0 : res.rows) || [];
|
|
424
|
+
}));
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
saveUserError(action, message, data, createdAt, crowdinId, integrationId) {
|
|
428
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
429
|
+
yield this.dbPromise;
|
|
430
|
+
yield this.executeQuery((client) => client.query('INSERT INTO user_errors(action, message, data, created_at, integration_id, crowdin_id) VALUES ($1, $2, $3, $4, $5, $6)', [action, message, data, createdAt, integrationId, crowdinId]));
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
deleteUserErrors(createdAt, crowdinId, integrationId) {
|
|
434
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
435
|
+
yield this.dbPromise;
|
|
436
|
+
yield this.executeQuery((client) => {
|
|
437
|
+
let whereIntegrationCondition = 'integration_id is NULL';
|
|
438
|
+
const params = [createdAt, crowdinId];
|
|
439
|
+
if (integrationId) {
|
|
440
|
+
whereIntegrationCondition = 'integration_id = $3';
|
|
441
|
+
params.push(integrationId);
|
|
442
|
+
}
|
|
443
|
+
return client.query(`DELETE FROM user_errors WHERE created_at < $1 AND crowdin_id = $2 AND ${whereIntegrationCondition}`, params);
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
}
|
|
398
447
|
}
|
|
399
448
|
exports.PostgreStorage = PostgreStorage;
|
package/out/storage/sqlite.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Storage } from '.';
|
|
2
|
-
import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks } from '../models';
|
|
2
|
+
import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, UserErrors } from '../models';
|
|
3
3
|
export interface SQLiteStorageConfig {
|
|
4
4
|
dbFolder: string;
|
|
5
5
|
}
|
|
@@ -45,4 +45,7 @@ export declare class SQLiteStorage implements Storage {
|
|
|
45
45
|
getWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<IntegrationWebhooks | undefined>;
|
|
46
46
|
saveWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<void>;
|
|
47
47
|
deleteWebhooks(fileIds: any[], integrationId: string, crowdinId: string, provider: string): Promise<void>;
|
|
48
|
+
getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[]>;
|
|
49
|
+
saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
|
|
50
|
+
deleteUserErrors(createAt: string, crowdinId: string, integrationId?: string): Promise<void>;
|
|
48
51
|
}
|
package/out/storage/sqlite.js
CHANGED
|
@@ -195,6 +195,18 @@ class SQLiteStorage {
|
|
|
195
195
|
crowdin_id varchar not null,
|
|
196
196
|
provider varchar not null
|
|
197
197
|
);
|
|
198
|
+
`, []);
|
|
199
|
+
yield this._run(`
|
|
200
|
+
create table if not exists user_errors
|
|
201
|
+
(
|
|
202
|
+
id integer not null primary key autoincrement,
|
|
203
|
+
action varchar not null,
|
|
204
|
+
message varchar not null,
|
|
205
|
+
data varchar null,
|
|
206
|
+
created_at varchar not null,
|
|
207
|
+
crowdin_id varchar not null,
|
|
208
|
+
integration_id varchar null
|
|
209
|
+
);
|
|
198
210
|
`, []);
|
|
199
211
|
this._res && this._res();
|
|
200
212
|
// TODO: temporary code
|
|
@@ -251,6 +263,7 @@ class SQLiteStorage {
|
|
|
251
263
|
yield this.run('DELETE FROM app_metadata WHERE crowdin_id = ?', [id]);
|
|
252
264
|
yield this.run('DELETE FROM files_snapshot WHERE crowdin_id = ?', [id]);
|
|
253
265
|
yield this.run('DELETE FROM webhooks WHERE crowdin_id = ?', [id]);
|
|
266
|
+
yield this.run('DELETE FROM user_errors WHERE crowdin_id = ?', [id]);
|
|
254
267
|
});
|
|
255
268
|
}
|
|
256
269
|
saveIntegrationCredentials(id, credentials, crowdinId) {
|
|
@@ -291,6 +304,7 @@ class SQLiteStorage {
|
|
|
291
304
|
yield this.run('DELETE FROM sync_settings where crowdin_id = ?', [crowdinId]);
|
|
292
305
|
yield this.run('DELETE FROM files_snapshot where crowdin_id = ?', [crowdinId]);
|
|
293
306
|
yield this.run('DELETE FROM webhooks where crowdin_id = ?', [crowdinId]);
|
|
307
|
+
yield this.run('DELETE FROM user_errors where crowdin_id = ?', [crowdinId]);
|
|
294
308
|
});
|
|
295
309
|
}
|
|
296
310
|
saveMetadata(id, metadata, crowdinId) {
|
|
@@ -389,5 +403,28 @@ class SQLiteStorage {
|
|
|
389
403
|
return this.run(`DELETE FROM webhooks WHERE file_id IN (${placeholders}) AND integration_id = ? AND crowdin_id = ? AND provider = ?`, [...fileIds, integrationId, crowdinId, provider]);
|
|
390
404
|
});
|
|
391
405
|
}
|
|
406
|
+
getAllUserErrors(crowdinId, integrationId) {
|
|
407
|
+
let whereIntegrationCondition = 'integration_id is NULL';
|
|
408
|
+
const params = [crowdinId];
|
|
409
|
+
if (integrationId) {
|
|
410
|
+
whereIntegrationCondition = 'integration_id = ?';
|
|
411
|
+
params.push(integrationId);
|
|
412
|
+
}
|
|
413
|
+
return this.each(`SELECT id, action, message, data, created_at as createdAt, integration_id as integrationId, crowdin_id as crowdinId FROM user_errors WHERE crowdin_id = ? AND ${whereIntegrationCondition}`, params);
|
|
414
|
+
}
|
|
415
|
+
saveUserError(action, message, data, createdAt, crowdinId, integrationId) {
|
|
416
|
+
return this.run('INSERT INTO user_errors(action, message, data, created_at, integration_id, crowdin_id) VALUES (?, ?, ?, ?, ?, ?)', [action, message, data, createdAt, integrationId, crowdinId]);
|
|
417
|
+
}
|
|
418
|
+
deleteUserErrors(createAt, crowdinId, integrationId) {
|
|
419
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
420
|
+
let whereIntegrationCondition = 'integration_id is NULL';
|
|
421
|
+
const params = [createAt, crowdinId];
|
|
422
|
+
if (integrationId) {
|
|
423
|
+
whereIntegrationCondition = 'integration_id = ?';
|
|
424
|
+
params.push(integrationId);
|
|
425
|
+
}
|
|
426
|
+
return this.run(`DELETE FROM user_errors WHERE created_at < ? AND crowdin_id = ? AND ${whereIntegrationCondition}`, params);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
392
429
|
}
|
|
393
430
|
exports.SQLiteStorage = SQLiteStorage;
|
package/out/util/logger.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ interface CustomAxiosError extends AxiosError {
|
|
|
20
20
|
}
|
|
21
21
|
export declare class AppModuleError extends Error {
|
|
22
22
|
data: any;
|
|
23
|
+
appData: any;
|
|
23
24
|
isAxiosError: boolean;
|
|
24
25
|
constructor(error: CustomAxiosError | Error | string, data?: any);
|
|
25
26
|
}
|
|
@@ -27,4 +28,10 @@ export declare class AppModuleAggregateError extends Error {
|
|
|
27
28
|
errors: Error[] | AppModuleError[];
|
|
28
29
|
constructor(errors: Error[] | AppModuleError[], message: string);
|
|
29
30
|
}
|
|
31
|
+
export declare function handleUserError({ action, error, crowdinId, clientId, }: {
|
|
32
|
+
action: string;
|
|
33
|
+
error: Error | string;
|
|
34
|
+
crowdinId: string;
|
|
35
|
+
clientId?: string;
|
|
36
|
+
}): Promise<void>;
|
|
30
37
|
export {};
|
package/out/util/logger.js
CHANGED
|
@@ -22,9 +22,19 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
25
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.AppModuleAggregateError = exports.AppModuleError = exports.getErrorMessage = exports.isAxiosError = exports.logError = exports.log = exports.withContextError = exports.withContext = exports.prepareContext = exports.initialize = void 0;
|
|
35
|
+
exports.handleUserError = exports.AppModuleAggregateError = exports.AppModuleError = exports.getErrorMessage = exports.isAxiosError = exports.logError = exports.log = exports.withContextError = exports.withContext = exports.prepareContext = exports.initialize = void 0;
|
|
27
36
|
const logsFormatter = __importStar(require("@crowdin/logs-formatter"));
|
|
37
|
+
const storage_1 = require("../storage");
|
|
28
38
|
let logConfig;
|
|
29
39
|
let onError;
|
|
30
40
|
function initialize(config) {
|
|
@@ -139,10 +149,10 @@ function errorOutputByType(error) {
|
|
|
139
149
|
status: ((_j = error.response) === null || _j === void 0 ? void 0 : _j.status) || ((_k = error.data) === null || _k === void 0 ? void 0 : _k.response.status),
|
|
140
150
|
}
|
|
141
151
|
: {};
|
|
142
|
-
console.error(message, { attributes: { request, response } }, { backtrace: error.stack });
|
|
152
|
+
console.error(message, { attributes: JSON.stringify({ request, response }) }, { backtrace: error.stack });
|
|
143
153
|
}
|
|
144
154
|
else {
|
|
145
|
-
console.error(message, error.data ? { attributes: error.data } : '', error.stack ? { backtrace: error.stack } : '');
|
|
155
|
+
console.error(message, error.data ? { attributes: typeof error.data === 'object' ? JSON.stringify(error.data) : error.data } : '', error.stack ? { backtrace: error.stack } : '');
|
|
146
156
|
}
|
|
147
157
|
}
|
|
148
158
|
function isAxiosError(e) {
|
|
@@ -168,8 +178,11 @@ class AppModuleError extends Error {
|
|
|
168
178
|
super(typeof error === 'string' ? error : error.message);
|
|
169
179
|
this.isAxiosError = false;
|
|
170
180
|
this.name = 'AppModuleError';
|
|
171
|
-
this.data = typeof error === 'string' ? Object.assign({}, data) : Object.assign(Object.assign({}, error), data);
|
|
172
181
|
this.stack = typeof error === 'string' ? this.stack : error.stack;
|
|
182
|
+
this.appData = data;
|
|
183
|
+
if (typeof error !== 'string') {
|
|
184
|
+
this.data = Object.assign({}, error);
|
|
185
|
+
}
|
|
173
186
|
if (isAxiosError(error)) {
|
|
174
187
|
this.isAxiosError = true;
|
|
175
188
|
}
|
|
@@ -184,3 +197,76 @@ class AppModuleAggregateError extends Error {
|
|
|
184
197
|
}
|
|
185
198
|
}
|
|
186
199
|
exports.AppModuleAggregateError = AppModuleAggregateError;
|
|
200
|
+
function storeUserError({ action, error, crowdinId, clientId, }) {
|
|
201
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
|
202
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
203
|
+
const data = {};
|
|
204
|
+
if (isAxiosError(error)) {
|
|
205
|
+
data.requestParams = {
|
|
206
|
+
method: ((_a = error === null || error === void 0 ? void 0 : error.request) === null || _a === void 0 ? void 0 : _a.method) || ((_c = (_b = error === null || error === void 0 ? void 0 : error.data) === null || _b === void 0 ? void 0 : _b.request) === null || _c === void 0 ? void 0 : _c.method),
|
|
207
|
+
protocol: ((_d = error === null || error === void 0 ? void 0 : error.request) === null || _d === void 0 ? void 0 : _d.protocol) || ((_f = (_e = error === null || error === void 0 ? void 0 : error.data) === null || _e === void 0 ? void 0 : _e.request) === null || _f === void 0 ? void 0 : _f.protocol),
|
|
208
|
+
host: ((_g = error === null || error === void 0 ? void 0 : error.request) === null || _g === void 0 ? void 0 : _g.host) || ((_j = (_h = error === null || error === void 0 ? void 0 : error.data) === null || _h === void 0 ? void 0 : _h.request) === null || _j === void 0 ? void 0 : _j.host),
|
|
209
|
+
path: ((_k = error === null || error === void 0 ? void 0 : error.request) === null || _k === void 0 ? void 0 : _k.path) || ((_m = (_l = error === null || error === void 0 ? void 0 : error.data) === null || _l === void 0 ? void 0 : _l.request) === null || _m === void 0 ? void 0 : _m.path),
|
|
210
|
+
};
|
|
211
|
+
data.responseData = {
|
|
212
|
+
data: ((_o = error === null || error === void 0 ? void 0 : error.response) === null || _o === void 0 ? void 0 : _o.data) || ((_q = (_p = error === null || error === void 0 ? void 0 : error.data) === null || _p === void 0 ? void 0 : _p.response) === null || _q === void 0 ? void 0 : _q.data),
|
|
213
|
+
status: ((_r = error === null || error === void 0 ? void 0 : error.response) === null || _r === void 0 ? void 0 : _r.status) || ((_t = (_s = error === null || error === void 0 ? void 0 : error.data) === null || _s === void 0 ? void 0 : _s.response) === null || _t === void 0 ? void 0 : _t.status),
|
|
214
|
+
statusText: ((_u = error === null || error === void 0 ? void 0 : error.response) === null || _u === void 0 ? void 0 : _u.statusText) || ((_w = (_v = error === null || error === void 0 ? void 0 : error.data) === null || _v === void 0 ? void 0 : _v.response) === null || _w === void 0 ? void 0 : _w.statusText),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (error instanceof AppModuleError && error.appData) {
|
|
218
|
+
data.appData = error.appData;
|
|
219
|
+
}
|
|
220
|
+
yield (0, storage_1.getStorage)().saveUserError(action, error.message, JSON.stringify(data), `${Date.now()}`, crowdinId, clientId);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
function clearOldUserErrors(crowdinId, clientId) {
|
|
224
|
+
const date = new Date();
|
|
225
|
+
date.setMonth(date.getMonth() - 1); // previous month
|
|
226
|
+
(0, storage_1.getStorage)().deleteUserErrors(`${date.getTime()}`, crowdinId, clientId);
|
|
227
|
+
}
|
|
228
|
+
function mergeAppModuleAggregateErrors(errors) {
|
|
229
|
+
const result = [];
|
|
230
|
+
const mergedData = {};
|
|
231
|
+
for (const errorItem of errors) {
|
|
232
|
+
if (errorItem instanceof AppModuleError) {
|
|
233
|
+
if (typeof errorItem.appData === 'object' &&
|
|
234
|
+
errorItem.appData !== null &&
|
|
235
|
+
!Array.isArray(errorItem.appData)) {
|
|
236
|
+
if (!mergedData[errorItem.message]) {
|
|
237
|
+
mergedData[errorItem.message] = {};
|
|
238
|
+
}
|
|
239
|
+
for (const key in errorItem.appData) {
|
|
240
|
+
mergedData[errorItem.message][key] = (mergedData[errorItem.message][key] || []).concat(errorItem.appData[key]);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else if (errorItem.appData) {
|
|
244
|
+
mergedData[errorItem.message] = (mergedData[errorItem.message] || []).concat(errorItem.appData);
|
|
245
|
+
}
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
result.push(errorItem);
|
|
249
|
+
}
|
|
250
|
+
for (const key in mergedData) {
|
|
251
|
+
result.push(new AppModuleError(key, mergedData[key]));
|
|
252
|
+
}
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
function handleUserError({ action, error, crowdinId, clientId, }) {
|
|
256
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
257
|
+
if (typeof error === 'string') {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (error instanceof AppModuleAggregateError) {
|
|
261
|
+
const mergedErrors = mergeAppModuleAggregateErrors(error.errors);
|
|
262
|
+
for (const key in mergedErrors) {
|
|
263
|
+
yield handleUserError({ action, error: mergedErrors[key], crowdinId, clientId });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
yield storeUserError({ action, error, crowdinId, clientId });
|
|
268
|
+
clearOldUserErrors(crowdinId, clientId);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
exports.handleUserError = handleUserError;
|
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
</div>
|
|
34
34
|
</crowdin-alert>
|
|
35
35
|
{{/if}}
|
|
36
|
-
<div>
|
|
36
|
+
<div id="buttons">
|
|
37
|
+
<crowdin-button id="show-integration-btn" class="hidden" icon-before="arrow_back" onclick="showIntegration();">Integration</crowdin-button>
|
|
38
|
+
<crowdin-button id="show-error-logs-btn" icon-before="list" onclick="showErrorLogs();">Error logs</crowdin-button>
|
|
37
39
|
{{#if infoModal}}
|
|
38
40
|
<crowdin-button icon-before="info" onclick="infoModal.open();">{{infoModal.title}}</crowdin-button>
|
|
39
41
|
{{/if}}
|
|
@@ -87,6 +89,16 @@
|
|
|
87
89
|
{{/if}}
|
|
88
90
|
>
|
|
89
91
|
</crowdin-simple-integration>
|
|
92
|
+
<div id="user-errors" class="hidden">
|
|
93
|
+
<crowdin-alert title="Error Logs">This table displays the most recent error logs from the past month. Logs older than one month will be automatically deleted.</crowdin-alert>
|
|
94
|
+
<crowdin-show-as-table
|
|
95
|
+
is-loading
|
|
96
|
+
id="user-errors-table"
|
|
97
|
+
is-searchable
|
|
98
|
+
total-records="25"
|
|
99
|
+
search-placeholder="Search something"
|
|
100
|
+
></crowdin-show-as-table>
|
|
101
|
+
</div>
|
|
90
102
|
</div>
|
|
91
103
|
<crowdin-toasts></crowdin-toasts>
|
|
92
104
|
<crowdin-modal id="subscription-modal" modal-width="50" close-button="false">
|
|
@@ -256,6 +268,13 @@
|
|
|
256
268
|
</div>
|
|
257
269
|
</crowdin-modal>
|
|
258
270
|
{{/or}}
|
|
271
|
+
|
|
272
|
+
<crowdin-modal
|
|
273
|
+
id="user-error-detail"
|
|
274
|
+
close-button-title="Close"
|
|
275
|
+
close-button="true"
|
|
276
|
+
>
|
|
277
|
+
</crowdin-modal>
|
|
259
278
|
</body>
|
|
260
279
|
<script type="text/javascript">
|
|
261
280
|
document.body.addEventListener('refreshFilesList', (e) => {
|
|
@@ -491,95 +510,94 @@
|
|
|
491
510
|
}
|
|
492
511
|
|
|
493
512
|
{{#if configurationFields}}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
513
|
+
const settingsModal = document.getElementById('settings-modal');
|
|
514
|
+
const settingsSaveBtn = document.getElementById('settings-save-btn');
|
|
515
|
+
let config = JSON.parse('{{{config}}}');
|
|
516
|
+
|
|
517
|
+
function triggerEvent(el, type) {
|
|
518
|
+
const e = document.createEvent('HTMLEvents');
|
|
519
|
+
e.initEvent(type, false, true);
|
|
520
|
+
el.dispatchEvent(e);
|
|
521
|
+
}
|
|
503
522
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
523
|
+
function fillSettingsForm() {
|
|
524
|
+
Object.entries(config).forEach(([key, value]) => {
|
|
525
|
+
const el = document.getElementById(`${key}-settings`);
|
|
526
|
+
if (el && (value || el.tagName.toLowerCase() === 'crowdin-checkbox')) {
|
|
527
|
+
if (el.tagName.toLowerCase() === 'crowdin-select') {
|
|
528
|
+
if (el.hasAttribute('is-multi')) {
|
|
529
|
+
el.value = JSON.stringify(value);
|
|
530
|
+
} else {
|
|
531
|
+
el.value = JSON.stringify([value]);
|
|
532
|
+
}
|
|
533
|
+
} else if (el.tagName.toLowerCase() === 'crowdin-checkbox') {
|
|
534
|
+
el.checked = !!value;
|
|
511
535
|
} else {
|
|
512
|
-
el.value =
|
|
536
|
+
el.value = value;
|
|
513
537
|
}
|
|
514
|
-
} else if (el.tagName.toLowerCase() === 'crowdin-checkbox') {
|
|
515
|
-
el.checked = !!value;
|
|
516
|
-
} else {
|
|
517
|
-
el.value = value;
|
|
518
|
-
}
|
|
519
538
|
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
function saveSettings() {
|
|
526
|
-
setLoader();
|
|
527
|
-
const settingsElements = Array.from(document.getElementById('modal-content').children);
|
|
528
|
-
const tags = ['crowdin-checkbox', 'crowdin-select', 'crowdin-input'];
|
|
529
|
-
const configReq = {};
|
|
530
|
-
settingsElements
|
|
531
|
-
.filter(e => tags.includes(e.tagName.toLowerCase()))
|
|
532
|
-
.forEach(e => {
|
|
533
|
-
const key = e.getAttribute('key');
|
|
534
|
-
let value;
|
|
535
|
-
if (e.tagName.toLowerCase() === 'crowdin-select') {
|
|
536
|
-
value = JSON.parse(e.value);
|
|
537
|
-
if (!e.hasAttribute('is-multi')) {
|
|
538
|
-
value = value.length > 0 ? value[0] : undefined;
|
|
539
|
-
}
|
|
540
|
-
} else if (e.tagName.toLowerCase() === 'crowdin-checkbox') {
|
|
541
|
-
value = !!e.checked;
|
|
542
|
-
} else {
|
|
543
|
-
value = e.value;
|
|
539
|
+
triggerEvent(el, 'change');
|
|
544
540
|
}
|
|
545
|
-
configReq[key] = value;
|
|
546
541
|
});
|
|
547
|
-
|
|
548
|
-
checkOrigin()
|
|
549
|
-
.then(restParams => fetch('api/settings' + restParams, {
|
|
550
|
-
method: 'POST',
|
|
551
|
-
headers: { 'Content-Type': 'application/json' },
|
|
552
|
-
body: JSON.stringify({ config: configReq })
|
|
553
|
-
}))
|
|
554
|
-
.then(checkResponse)
|
|
555
|
-
.then(() => {
|
|
556
|
-
showToast('Settings successfully saved');
|
|
557
|
-
config = configReq;
|
|
558
|
-
})
|
|
559
|
-
.catch(e => catchRejection(e, 'Can\'t save settings'))
|
|
560
|
-
.finally(() => {
|
|
561
|
-
unsetLoader();
|
|
562
|
-
settingsSaveBtn.removeAttribute('disabled');
|
|
563
|
-
settingsModal.close();
|
|
564
|
-
{{#if reloadOnConfigSave}}
|
|
565
|
-
getIntegrationData(true);
|
|
566
|
-
getCrowdinData();
|
|
567
|
-
{{/if}}
|
|
568
|
-
});
|
|
569
|
-
}
|
|
542
|
+
}
|
|
570
543
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
544
|
+
function saveSettings() {
|
|
545
|
+
setLoader();
|
|
546
|
+
const settingsElements = Array.from(document.getElementById('modal-content').children);
|
|
547
|
+
const tags = ['crowdin-checkbox', 'crowdin-select', 'crowdin-input'];
|
|
548
|
+
const configReq = {};
|
|
549
|
+
settingsElements
|
|
550
|
+
.filter(e => tags.includes(e.tagName.toLowerCase()))
|
|
551
|
+
.forEach(e => {
|
|
552
|
+
const key = e.getAttribute('key');
|
|
553
|
+
let value;
|
|
554
|
+
if (e.tagName.toLowerCase() === 'crowdin-select') {
|
|
555
|
+
value = JSON.parse(e.value);
|
|
556
|
+
if (!e.hasAttribute('is-multi')) {
|
|
557
|
+
value = value.length > 0 ? value[0] : undefined;
|
|
558
|
+
}
|
|
559
|
+
} else if (e.tagName.toLowerCase() === 'crowdin-checkbox') {
|
|
560
|
+
value = !!e.checked;
|
|
561
|
+
} else {
|
|
562
|
+
value = e.value;
|
|
563
|
+
}
|
|
564
|
+
configReq[key] = value;
|
|
565
|
+
});
|
|
566
|
+
settingsSaveBtn.setAttribute('disabled', true);
|
|
567
|
+
checkOrigin()
|
|
568
|
+
.then(restParams => fetch('api/settings' + restParams, {
|
|
569
|
+
method: 'POST',
|
|
570
|
+
headers: { 'Content-Type': 'application/json' },
|
|
571
|
+
body: JSON.stringify({ config: configReq })
|
|
572
|
+
}))
|
|
573
|
+
.then(checkResponse)
|
|
574
|
+
.then(() => {
|
|
575
|
+
showToast('Settings successfully saved');
|
|
576
|
+
config = configReq;
|
|
577
|
+
})
|
|
578
|
+
.catch(e => catchRejection(e, 'Can\'t save settings'))
|
|
579
|
+
.finally(() => {
|
|
580
|
+
unsetLoader();
|
|
581
|
+
settingsSaveBtn.removeAttribute('disabled');
|
|
582
|
+
settingsModal.close();
|
|
583
|
+
{{#if reloadOnConfigSave}}
|
|
584
|
+
getIntegrationData(true);
|
|
585
|
+
getCrowdinData();
|
|
586
|
+
{{/if}}
|
|
587
|
+
});
|
|
588
|
+
}
|
|
575
589
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}, 500)
|
|
581
|
-
}
|
|
590
|
+
function setLoader() {
|
|
591
|
+
const loader = document.querySelector('#settings-modal .loader');
|
|
592
|
+
loader.classList.remove('hidden');
|
|
593
|
+
}
|
|
582
594
|
|
|
595
|
+
function unsetLoader() {
|
|
596
|
+
const loader = document.querySelector('#settings-modal .loader');
|
|
597
|
+
setTimeout(function() {
|
|
598
|
+
loader.classList.add('hidden');
|
|
599
|
+
}, 500)
|
|
600
|
+
}
|
|
583
601
|
{{else}}
|
|
584
602
|
const settingsModal = undefined;
|
|
585
603
|
{{/if}}
|
|
@@ -807,6 +825,95 @@
|
|
|
807
825
|
const notice = document.getElementById('notice');
|
|
808
826
|
checkAlert(notice, 'notice');
|
|
809
827
|
{{/if}}
|
|
828
|
+
|
|
829
|
+
function showErrorLogs() {
|
|
830
|
+
document.getElementById('show-error-logs-btn').classList.add('hidden');
|
|
831
|
+
document.getElementById('show-integration-btn').classList.remove('hidden');
|
|
832
|
+
|
|
833
|
+
appComponent.classList.add('hidden');
|
|
834
|
+
document.getElementById('user-errors').classList.remove('hidden');
|
|
835
|
+
|
|
836
|
+
checkOrigin()
|
|
837
|
+
.then(restParams => fetch('api/user-errors' + restParams))
|
|
838
|
+
.then(checkResponse)
|
|
839
|
+
.then((res) => {
|
|
840
|
+
const table = document.getElementById('user-errors-table');
|
|
841
|
+
|
|
842
|
+
const clickRow = (field, index, item) => {
|
|
843
|
+
const modal = document.getElementById('user-error-detail');
|
|
844
|
+
modal.open();
|
|
845
|
+
modal.innerHTML = getUserErrorDetail(item);
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
table.setTableConfig && table.setTableConfig({
|
|
849
|
+
defaultSortingColumn: "createdAt",
|
|
850
|
+
defaultSortingDir: "desc",
|
|
851
|
+
headerFields: [
|
|
852
|
+
{
|
|
853
|
+
field: "createdAt",
|
|
854
|
+
name: "Date",
|
|
855
|
+
isSortable: true,
|
|
856
|
+
clickFn: clickRow,
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
field: "action",
|
|
860
|
+
name: "Action",
|
|
861
|
+
clickFn: clickRow,
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
field: "message",
|
|
865
|
+
name: "Message",
|
|
866
|
+
clickFn: clickRow,
|
|
867
|
+
}
|
|
868
|
+
],
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
table.setTableData(JSON.stringify(res));
|
|
872
|
+
table.setAttribute(`is-loading`, false);
|
|
873
|
+
})
|
|
874
|
+
.catch(e => catchRejection(e, 'Can\'t fetch error logs'));
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function showIntegration() {
|
|
878
|
+
document.getElementById('show-error-logs-btn').classList.remove('hidden');
|
|
879
|
+
document.getElementById('show-integration-btn').classList.add('hidden');
|
|
880
|
+
|
|
881
|
+
appComponent.classList.remove('hidden');
|
|
882
|
+
document.getElementById('user-errors').classList.add('hidden');
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function getUserErrorDetail(error) {
|
|
886
|
+
const data = JSON.parse(error.data);
|
|
887
|
+
|
|
888
|
+
let html = '<div class="error-detail-table"><table>';
|
|
889
|
+
html += `<tr><td>Action</td><td>${error.action}</td></tr>`;
|
|
890
|
+
html += `<tr><td>Message</td><td>${error.message}</td></tr>`;
|
|
891
|
+
html += `<tr><td>Date/time</td><td>${error.createdAt}</td></tr>`;
|
|
892
|
+
|
|
893
|
+
if (data.requestParams) {
|
|
894
|
+
html += `<tr><td>Method</td><td>${data.requestParams.method}</td></tr>`;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (data.responseData) {
|
|
898
|
+
html += `<tr><td>Response Data</td><td><pre>${JSON.stringify(data.responseData, null, 2)}</pre></td></tr>`;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
if (data.appData) {
|
|
902
|
+
if (Array.isArray(data.appData)) {
|
|
903
|
+
html += `<tr><td>App Data</td><td><pre>${JSON.stringify(data.appData, null, 2)}</pre></td></tr>`;
|
|
904
|
+
} else if (typeof data.appData === 'object') {
|
|
905
|
+
for (const key in data.appData) {
|
|
906
|
+
html += `<tr><td>${key.charAt(0).toUpperCase() + key.slice(1)}</td><td><pre>${JSON.stringify(data.appData[key], null, 2)}</pre></td></tr>`;
|
|
907
|
+
}
|
|
908
|
+
} else {
|
|
909
|
+
html += `<tr><td>App Data</td><td>${data.appData}</td></tr>`;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
html += '</table></div>';
|
|
914
|
+
|
|
915
|
+
return html;
|
|
916
|
+
}
|
|
810
917
|
</script>
|
|
811
918
|
|
|
812
919
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowdin/app-project-module",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
|
|
5
5
|
"main": "out/index.js",
|
|
6
6
|
"types": "out/index.d.ts",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@aws-sdk/client-s3": "^3.423.0",
|
|
18
18
|
"@aws-sdk/s3-request-presigner": "^3.423.0",
|
|
19
19
|
"@crowdin/crowdin-apps-functions": "0.6.0",
|
|
20
|
-
"@crowdin/logs-formatter": "^2.0.
|
|
20
|
+
"@crowdin/logs-formatter": "^2.0.6",
|
|
21
21
|
"@godaddy/terminus": "^4.12.1",
|
|
22
22
|
"@types/pg": "^8.10.3",
|
|
23
23
|
"amqplib": "^0.10.3",
|