@crowdin/app-project-module 3.2.2 → 3.3.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/modules/about.js +2 -10
- package/out/modules/api/api.js +7 -0
- package/out/modules/api/components.d.ts +6 -1
- package/out/modules/api/components.js +6 -1
- package/out/modules/integration/handlers/crowdin-update.js +33 -6
- package/out/modules/integration/handlers/integration-update.js +1 -1
- package/out/modules/integration/handlers/job-list.d.ts +8 -2
- package/out/modules/integration/handlers/job-list.js +30 -11
- package/out/modules/integration/types.d.ts +8 -0
- package/out/modules/integration/util/types.d.ts +2 -0
- package/out/static/css/styles.css +1 -1
- package/out/static/img/crowdin_small.svg +8 -7
- package/out/static/ui/about.bundle.js +11 -1
- package/out/static/ui/about.bundle.js.map +1 -1
- package/out/static/ui/error.bundle.js +3 -3
- package/out/static/ui/error.bundle.js.map +1 -1
- package/out/static/ui/login.bundle.js +4 -4
- package/out/static/ui/login.bundle.js.map +1 -1
- package/out/static/ui/main.bundle.js +656 -590
- package/out/static/ui/main.bundle.js.map +1 -1
- package/out/static/ui/subscription.bundle.js +1 -1
- package/out/static/ui/subscription.bundle.js.map +1 -1
- package/out/storage/drizzle/index.js +6 -1
- package/out/util/index.d.ts +1 -0
- package/out/util/index.js +16 -1
- package/out/util/logo.d.ts +4 -0
- package/out/util/logo.js +47 -0
- package/out/util/static-files.d.ts +4 -1
- package/out/util/static-files.js +26 -4
- package/package.json +1 -1
package/out/modules/about.js
CHANGED
|
@@ -19,17 +19,9 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
19
19
|
const util_1 = require("../util");
|
|
20
20
|
const crowdin_client_1 = require("../middlewares/crowdin-client");
|
|
21
21
|
const jsx_renderer_1 = require("../util/jsx-renderer");
|
|
22
|
-
function escapeHtml(value) {
|
|
23
|
-
return value
|
|
24
|
-
.replace(/&/g, '&')
|
|
25
|
-
.replace(/</g, '<')
|
|
26
|
-
.replace(/>/g, '>')
|
|
27
|
-
.replace(/"/g, '"')
|
|
28
|
-
.replace(/'/g, ''');
|
|
29
|
-
}
|
|
30
22
|
function getAboutPageHead({ name, storeLink }) {
|
|
31
|
-
const escapedName = escapeHtml(name);
|
|
32
|
-
const escapedStoreLink = storeLink ? escapeHtml(storeLink) : '';
|
|
23
|
+
const escapedName = (0, util_1.escapeHtml)(name);
|
|
24
|
+
const escapedStoreLink = storeLink ? (0, util_1.escapeHtml)(storeLink) : '';
|
|
33
25
|
return `
|
|
34
26
|
<head>
|
|
35
27
|
<title>${escapedName}</title>
|
package/out/modules/api/api.js
CHANGED
|
@@ -443,6 +443,13 @@ function addDefaultApiEndpoints(app, config) {
|
|
|
443
443
|
* default: 0
|
|
444
444
|
* minimum: 0
|
|
445
445
|
* example: 0
|
|
446
|
+
* - name: finished
|
|
447
|
+
* in: query
|
|
448
|
+
* required: false
|
|
449
|
+
* description: 'Filter by completion: true returns only finished jobs, false returns only active jobs. Omit to list all jobs'
|
|
450
|
+
* schema:
|
|
451
|
+
* type: boolean
|
|
452
|
+
* example: false
|
|
446
453
|
* responses:
|
|
447
454
|
* 200:
|
|
448
455
|
* description: 'List of jobs retrieved successfully'
|
|
@@ -121,7 +121,6 @@
|
|
|
121
121
|
* title: 'Sync Files to Crowdin'
|
|
122
122
|
* required:
|
|
123
123
|
* - projectId
|
|
124
|
-
* - files
|
|
125
124
|
* properties:
|
|
126
125
|
* projectId:
|
|
127
126
|
* description: 'Project Identifier. Get via [List Projects](https://developer.crowdin.com/api/v2/#operation/api.projects.getMany)'
|
|
@@ -129,6 +128,12 @@
|
|
|
129
128
|
* example: 12
|
|
130
129
|
* files:
|
|
131
130
|
* $ref: '#/components/schemas/IntegrationFiles'
|
|
131
|
+
* entryIds:
|
|
132
|
+
* description: 'List of integration file IDs to sync. The framework resolves these IDs to files via getIntegrationFiles and runs the normal sync path. Takes precedence over files when both are provided. Returns 501 if the integration does not support filtering by entry ID. Either files or entryIds is required for API calls.'
|
|
133
|
+
* type: array
|
|
134
|
+
* items:
|
|
135
|
+
* type: string
|
|
136
|
+
* example: ['cam_1', 'cam_2', 'cam_3']
|
|
132
137
|
* uploadTranslations:
|
|
133
138
|
* description: 'Upload exist translation from integration'
|
|
134
139
|
* type: boolean
|
|
@@ -122,7 +122,6 @@
|
|
|
122
122
|
* title: 'Sync Files to Crowdin'
|
|
123
123
|
* required:
|
|
124
124
|
* - projectId
|
|
125
|
-
* - files
|
|
126
125
|
* properties:
|
|
127
126
|
* projectId:
|
|
128
127
|
* description: 'Project Identifier. Get via [List Projects](https://developer.crowdin.com/api/v2/#operation/api.projects.getMany)'
|
|
@@ -130,6 +129,12 @@
|
|
|
130
129
|
* example: 12
|
|
131
130
|
* files:
|
|
132
131
|
* $ref: '#/components/schemas/IntegrationFiles'
|
|
132
|
+
* entryIds:
|
|
133
|
+
* description: 'List of integration file IDs to sync. The framework resolves these IDs to files via getIntegrationFiles and runs the normal sync path. Takes precedence over files when both are provided. Returns 501 if the integration does not support filtering by entry ID. Either files or entryIds is required for API calls.'
|
|
134
|
+
* type: array
|
|
135
|
+
* items:
|
|
136
|
+
* type: string
|
|
137
|
+
* example: ['cam_1', 'cam_2', 'cam_3']
|
|
133
138
|
* uploadTranslations:
|
|
134
139
|
* description: 'Upload exist translation from integration'
|
|
135
140
|
* type: boolean
|
|
@@ -21,7 +21,7 @@ const types_2 = require("../types");
|
|
|
21
21
|
const storage_1 = require("../../../storage");
|
|
22
22
|
function handle(config, integration) {
|
|
23
23
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
24
|
-
var _a, _b, _c, _d;
|
|
24
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
25
25
|
const projectId = req.crowdinContext.jwtPayload.context.project_id || (req === null || req === void 0 ? void 0 : req.body.projectId);
|
|
26
26
|
const uploadTranslations = req.query.uploadTranslations === 'true' || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.uploadTranslations);
|
|
27
27
|
const forcePushSources = req.query.forcePushSources === 'true' || ((_b = req.body) === null || _b === void 0 ? void 0 : _b.forcePushSources);
|
|
@@ -31,15 +31,42 @@ function handle(config, integration) {
|
|
|
31
31
|
if (rootFolder) {
|
|
32
32
|
req.logInfo(`Updating crowdin files under folder ${rootFolder.id}`);
|
|
33
33
|
}
|
|
34
|
-
if (req.isApiCall && !req.body.files) {
|
|
34
|
+
if (req.isApiCall && !req.body.files && !((_c = req.body.entryIds) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
35
35
|
return res.status(400).json({
|
|
36
36
|
error: {
|
|
37
|
-
message: 'Missing required parameter: files',
|
|
37
|
+
message: 'Missing required parameter: files or entryIds',
|
|
38
38
|
},
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
if ((_d = req.body.entryIds) === null || _d === void 0 ? void 0 : _d.length) {
|
|
42
|
+
if (integration.supportsEntryIdSync === false) {
|
|
43
|
+
return res.status(501).json({
|
|
44
|
+
error: { message: 'This integration does not support syncing by entry ID.' },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const entryIds = req.body.entryIds;
|
|
48
|
+
const resolved = yield integration.getIntegrationFiles({
|
|
49
|
+
credentials: req.integrationCredentials,
|
|
50
|
+
client: req.crowdinApiClient,
|
|
51
|
+
projectId,
|
|
52
|
+
settings: req.integrationSettings,
|
|
53
|
+
entryIds,
|
|
54
|
+
});
|
|
55
|
+
const files = (0, files_1.isExtendedResultType)(resolved) ? ((_e = resolved.data) !== null && _e !== void 0 ? _e : []) : resolved;
|
|
56
|
+
if (integration.supportsEntryIdSync !== true) {
|
|
57
|
+
const entryIdSet = new Set(entryIds);
|
|
58
|
+
if (files.some((f) => !entryIdSet.has(f.id))) {
|
|
59
|
+
return res.status(501).json({
|
|
60
|
+
error: {
|
|
61
|
+
message: 'This integration does not support syncing by entry ID. Use the files parameter instead.',
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
req.body = files;
|
|
67
|
+
}
|
|
41
68
|
// A request via API has a different structure
|
|
42
|
-
if (((
|
|
69
|
+
if (((_f = config.api) === null || _f === void 0 ? void 0 : _f.default) && req.body.files) {
|
|
43
70
|
req.body = req.body.files;
|
|
44
71
|
}
|
|
45
72
|
let payload = req.body;
|
|
@@ -72,7 +99,7 @@ function handle(config, integration) {
|
|
|
72
99
|
const excludedTargetLanguages = yield (0, files_1.getExcludedTargetLanguages)({
|
|
73
100
|
client: req.crowdinApiClient,
|
|
74
101
|
projectId,
|
|
75
|
-
languages: ((
|
|
102
|
+
languages: ((_g = req.query.languages) === null || _g === void 0 ? void 0 : _g.split(',')) || [],
|
|
76
103
|
});
|
|
77
104
|
yield (0, job_1.runAsJob)({
|
|
78
105
|
integrationId: req.crowdinContext.clientId,
|
|
@@ -128,7 +155,7 @@ function handle(config, integration) {
|
|
|
128
155
|
}
|
|
129
156
|
if (matchingWarnings.length > 0) {
|
|
130
157
|
req.logInfo(`Sync completed with warnings: ${matchingWarnings.length} file(s) could not be matched.`);
|
|
131
|
-
const error = new logger_2.AppModuleAggregateError(matchingWarnings, 'Finished with errors. See
|
|
158
|
+
const error = new logger_2.AppModuleAggregateError(matchingWarnings, 'Finished with errors. See logs.');
|
|
132
159
|
error.isPartialSuccess = true;
|
|
133
160
|
throw error;
|
|
134
161
|
}
|
|
@@ -102,7 +102,7 @@ function handle(config, integration) {
|
|
|
102
102
|
});
|
|
103
103
|
if (matchingWarnings.length > 0) {
|
|
104
104
|
req.logInfo(`Sync completed with warnings: ${matchingWarnings.length} file(s) could not be matched.`);
|
|
105
|
-
const error = new logger_2.AppModuleAggregateError(matchingWarnings, 'Finished with errors. See
|
|
105
|
+
const error = new logger_2.AppModuleAggregateError(matchingWarnings, 'Finished with errors. See logs.');
|
|
106
106
|
error.isPartialSuccess = true;
|
|
107
107
|
throw error;
|
|
108
108
|
}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
import { Response } from 'express';
|
|
2
|
-
export
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
export declare function parseJobListQuery(query?: Request['query']): {
|
|
3
|
+
limit: number;
|
|
4
|
+
offset: number;
|
|
5
|
+
finished: boolean | undefined;
|
|
6
|
+
invalidFinished: string | undefined;
|
|
7
|
+
};
|
|
8
|
+
export default function handle(): (req: Request | import("../../..").CrowdinClientRequest, res: Response, next: Function) => void;
|
|
@@ -9,31 +9,51 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.parseJobListQuery = parseJobListQuery;
|
|
12
13
|
exports.default = handle;
|
|
13
14
|
const util_1 = require("../../../util");
|
|
14
15
|
const storage_1 = require("../../../storage");
|
|
15
16
|
const types_1 = require("../util/types");
|
|
17
|
+
const DEFAULT_LIMIT = 25;
|
|
18
|
+
const MAX_LIMIT = 100;
|
|
19
|
+
function parseInteger(value, fallback) {
|
|
20
|
+
const parsed = Number.parseInt(`${value}`, 10);
|
|
21
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
22
|
+
}
|
|
23
|
+
function parseJobListQuery(query = {}) {
|
|
24
|
+
const finished = query.finished === undefined ? undefined : `${query.finished}`;
|
|
25
|
+
return {
|
|
26
|
+
limit: Math.min(Math.max(parseInteger(query.limit, DEFAULT_LIMIT), 1), MAX_LIMIT),
|
|
27
|
+
offset: Math.max(parseInteger(query.offset, 0), 0),
|
|
28
|
+
finished: finished === undefined ? undefined : finished === 'true',
|
|
29
|
+
invalidFinished: finished !== undefined && !['true', 'false'].includes(finished) ? finished : undefined,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
16
32
|
function handle() {
|
|
17
33
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
18
|
-
const limit
|
|
19
|
-
|
|
20
|
-
|
|
34
|
+
const { limit, offset, finished, invalidFinished } = parseJobListQuery(req.query);
|
|
35
|
+
if (invalidFinished !== undefined) {
|
|
36
|
+
return res.status(400).json({
|
|
37
|
+
error: { message: `Invalid finished value: ${invalidFinished}. Allowed values: true,false` },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
req.logInfo(`Get all jobs${finished !== undefined ? ` (finished: ${finished})` : ''}`);
|
|
21
41
|
const jobs = yield (0, storage_1.getStorage)().getAllJobs({
|
|
22
42
|
integrationId: req.crowdinContext.clientId,
|
|
23
43
|
crowdinId: req.crowdinContext.crowdinId,
|
|
24
44
|
limit,
|
|
25
45
|
offset,
|
|
46
|
+
finished,
|
|
47
|
+
excludeTypes: [types_1.JobType.INTEGRATION_SETTINGS_SAVE],
|
|
26
48
|
});
|
|
27
49
|
if (!jobs) {
|
|
28
50
|
req.logInfo('No jobs found');
|
|
29
51
|
res.send([]);
|
|
30
52
|
return;
|
|
31
53
|
}
|
|
32
|
-
const
|
|
33
|
-
.filter((job) => job.type !== types_1.JobType.INTEGRATION_SETTINGS_SAVE)
|
|
34
|
-
.map((job) => {
|
|
54
|
+
const result = jobs.map((job) => {
|
|
35
55
|
var _a;
|
|
36
|
-
|
|
56
|
+
return ({
|
|
37
57
|
id: job.id,
|
|
38
58
|
progress: job.progress,
|
|
39
59
|
status: job.status,
|
|
@@ -42,10 +62,9 @@ function handle() {
|
|
|
42
62
|
initiatedBy: (_a = job.initiatedBy) !== null && _a !== void 0 ? _a : null,
|
|
43
63
|
createdAt: new Date(+job.createdAt).toISOString(),
|
|
44
64
|
finishedAt: job.finishedAt ? new Date(+job.finishedAt).toISOString() : null,
|
|
45
|
-
};
|
|
46
|
-
return baseJob;
|
|
65
|
+
});
|
|
47
66
|
});
|
|
48
|
-
req.logInfo(`Returning ${
|
|
49
|
-
res.send(
|
|
67
|
+
req.logInfo(`Returning ${result.length} jobs`);
|
|
68
|
+
res.send(result);
|
|
50
69
|
}));
|
|
51
70
|
}
|
|
@@ -69,7 +69,15 @@ export interface IntegrationLogic extends ModuleKey {
|
|
|
69
69
|
search?: any;
|
|
70
70
|
page?: any;
|
|
71
71
|
paginationData?: any;
|
|
72
|
+
entryIds?: string[];
|
|
72
73
|
}) => Promise<TreeItem[] | ExtendedResult<TreeItem[]>>;
|
|
74
|
+
/**
|
|
75
|
+
* Indicates whether getIntegrationFiles supports filtering by entryIds.
|
|
76
|
+
* true — trusted; the 501 auto-detection guard is skipped.
|
|
77
|
+
* false — opted out; POST /crowdin-update returns 501 immediately when entryIds is supplied.
|
|
78
|
+
* undefined — auto-detected: if no returned file ID matches any requested entryId, returns 501.
|
|
79
|
+
*/
|
|
80
|
+
supportsEntryIdSync?: boolean;
|
|
73
81
|
/**
|
|
74
82
|
* function to update crowdin files (e.g. pull integration data to crowdin source files)
|
|
75
83
|
*/
|