@crowdin/app-project-module 0.12.4 → 0.13.1
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/README.md +11 -2
- package/out/handlers/custom-file-format/process.d.ts +2 -2
- package/out/handlers/custom-file-format/process.js +6 -6
- package/out/handlers/manifest.js +2 -1
- package/out/index.d.ts +1 -0
- package/out/index.js +10 -2
- package/out/middlewares/crowdin-client.js +12 -1
- package/out/models/index.d.ts +25 -2
- package/out/models/index.js +17 -1
- package/out/util/index.js +1 -4
- package/out/views/main.handlebars +10 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ In both options you will need to provide Crowdin App configuration file. Please
|
|
|
16
16
|
- `saveMetadata` to save metadata (may be associated with an organization, project, etc)
|
|
17
17
|
- `getMetadata` to get metadata
|
|
18
18
|
- `deleteMetadata` to delete metadata (usually useful in `onUninstall` hook)
|
|
19
|
+
- `getUserSettings` to get settings that users manage in the integration module
|
|
19
20
|
- `establishCrowdinConnection` method that accept jwt token that you may forward from module UI and it will return back Crowdin client instance and the context.
|
|
20
21
|
|
|
21
22
|
## Status
|
|
@@ -66,6 +67,10 @@ const configuration = {
|
|
|
66
67
|
baseUrl: 'https://123.ngrok.io',
|
|
67
68
|
clientId: 'clientId',
|
|
68
69
|
clientSecret: 'clientSecret',
|
|
70
|
+
scopes: [
|
|
71
|
+
crowdinModule.Scope.PROJECTS,
|
|
72
|
+
crowdinModule.Scope.TRANSLATION_MEMORIES
|
|
73
|
+
],
|
|
69
74
|
name: 'Sample App',
|
|
70
75
|
identifier: 'sample-app',
|
|
71
76
|
description: 'Sample App description',
|
|
@@ -73,6 +78,10 @@ const configuration = {
|
|
|
73
78
|
imagePath: __dirname + '/' + 'logo.png',
|
|
74
79
|
integration: {
|
|
75
80
|
withRootFolder: true,
|
|
81
|
+
withCronSync: {
|
|
82
|
+
crowdin: true,
|
|
83
|
+
integration: true,
|
|
84
|
+
},
|
|
76
85
|
getIntegrationFiles: async (credentials, appSettings) => {
|
|
77
86
|
//here you need to fetch files/objects from integration
|
|
78
87
|
return [
|
|
@@ -427,14 +436,14 @@ const configuration = {
|
|
|
427
436
|
signaturePatterns: {
|
|
428
437
|
fileName: '^.+\.xml$'
|
|
429
438
|
},
|
|
430
|
-
parseFile: async (file, req) => {
|
|
439
|
+
parseFile: async (file, req, client, context, projectId) => {
|
|
431
440
|
const xml = convert.xml2json(file, { compact: true, spaces: 4 });
|
|
432
441
|
const fileContent = JSON.parse(xml);
|
|
433
442
|
//parse logic
|
|
434
443
|
const strings = [];
|
|
435
444
|
return { strings };
|
|
436
445
|
},
|
|
437
|
-
buildFile: async (file, req, strings) => {
|
|
446
|
+
buildFile: async (file, req, strings, client, context, projectId) => {
|
|
438
447
|
const xml = convert.xml2json(file, { compact: true, spaces: 4 });
|
|
439
448
|
const fileContent = JSON.parse(xml);
|
|
440
449
|
//build logic
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/// <reference types="qs" />
|
|
2
|
-
import {
|
|
2
|
+
import { Response } from 'express';
|
|
3
3
|
import { Config, CustomFileFormatLogic } from '../../models';
|
|
4
|
-
export default function handle(baseConfig: Config, baseUrl: string, folder: string, config: CustomFileFormatLogic): (req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
4
|
+
export default function handle(baseConfig: Config, baseUrl: string, folder: string, config: CustomFileFormatLogic): (req: 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;
|
|
@@ -29,7 +29,7 @@ function storeFile(fileContent, folder) {
|
|
|
29
29
|
}
|
|
30
30
|
}));
|
|
31
31
|
}
|
|
32
|
-
function handleBuildFile(baseUrl, dataFolder, config, file, req) {
|
|
32
|
+
function handleBuildFile(baseUrl, dataFolder, config, file, req, client, context, projectId) {
|
|
33
33
|
return __awaiter(this, void 0, void 0, function* () {
|
|
34
34
|
const response = {};
|
|
35
35
|
if (!req.strings && !req.stringsUrl) {
|
|
@@ -42,7 +42,7 @@ function handleBuildFile(baseUrl, dataFolder, config, file, req) {
|
|
|
42
42
|
else {
|
|
43
43
|
strings = (yield axios_1.default.get(req.stringsUrl)).data;
|
|
44
44
|
}
|
|
45
|
-
const res = yield config.buildFile(file, req, strings);
|
|
45
|
+
const res = yield config.buildFile(file, req, strings, client, context, projectId);
|
|
46
46
|
const contentFileEncoded = Buffer.from(res.contentFile).toString('base64');
|
|
47
47
|
if (Buffer.byteLength(contentFileEncoded, 'utf8') < MAX_BODY_SIZE) {
|
|
48
48
|
response.content = contentFileEncoded;
|
|
@@ -54,10 +54,10 @@ function handleBuildFile(baseUrl, dataFolder, config, file, req) {
|
|
|
54
54
|
return response;
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
|
-
function handleParseFile(baseUrl, dataFolder, config, file, req) {
|
|
57
|
+
function handleParseFile(baseUrl, dataFolder, config, file, req, client, context, projectId) {
|
|
58
58
|
return __awaiter(this, void 0, void 0, function* () {
|
|
59
59
|
const response = {};
|
|
60
|
-
const res = yield config.parseFile(file, req);
|
|
60
|
+
const res = yield config.parseFile(file, req, client, context, projectId);
|
|
61
61
|
if (res.previewFile) {
|
|
62
62
|
const previewFileEncoded = Buffer.from(res.previewFile).toString('base64');
|
|
63
63
|
if (Buffer.byteLength(previewFileEncoded, 'utf8') < MAX_BODY_SIZE) {
|
|
@@ -108,10 +108,10 @@ function handle(baseConfig, baseUrl, folder, config) {
|
|
|
108
108
|
let response = {};
|
|
109
109
|
switch (body.jobType) {
|
|
110
110
|
case models_1.ProcessFileJobType.BUILD_FILE:
|
|
111
|
-
response = yield handleBuildFile(baseFilesUrl, folder, config, file, body);
|
|
111
|
+
response = yield handleBuildFile(baseFilesUrl, folder, config, file, body, req.crowdinApiClient, req.crowdinContext, req.crowdinContext.jwtPayload.context.project_id);
|
|
112
112
|
break;
|
|
113
113
|
case models_1.ProcessFileJobType.PARSE_FILE:
|
|
114
|
-
response = yield handleParseFile(baseFilesUrl, folder, config, file, body);
|
|
114
|
+
response = yield handleParseFile(baseFilesUrl, folder, config, file, body, req.crowdinApiClient, req.crowdinContext, req.crowdinContext.jwtPayload.context.project_id);
|
|
115
115
|
break;
|
|
116
116
|
}
|
|
117
117
|
res.send({ data: response });
|
package/out/handlers/manifest.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const models_1 = require("../models");
|
|
3
4
|
function handle(config) {
|
|
4
5
|
const modules = {};
|
|
5
6
|
// TEMPORARY CODE drop on step 3 of CN-30453
|
|
@@ -179,7 +180,7 @@ function handle(config) {
|
|
|
179
180
|
installed: '/installed',
|
|
180
181
|
uninstall: '/uninstall',
|
|
181
182
|
},
|
|
182
|
-
scopes: [
|
|
183
|
+
scopes: config.scopes ? config.scopes : [models_1.Scope.PROJECTS],
|
|
183
184
|
modules,
|
|
184
185
|
};
|
|
185
186
|
res.send(manifest);
|
package/out/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Express } from 'express';
|
|
2
2
|
import { Config, CrowdinAppUtilities } from './models';
|
|
3
|
+
export { Scope } from './models';
|
|
3
4
|
export declare function addCrowdinEndpoints(app: Express, config: Config): CrowdinAppUtilities;
|
|
4
5
|
export declare function createApp(config: Config): void;
|
package/out/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
31
31
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
32
|
};
|
|
33
33
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
-
exports.createApp = exports.addCrowdinEndpoints = void 0;
|
|
34
|
+
exports.createApp = exports.addCrowdinEndpoints = exports.Scope = void 0;
|
|
35
35
|
const express_1 = __importDefault(require("express"));
|
|
36
36
|
const express_handlebars_1 = __importDefault(require("express-handlebars"));
|
|
37
37
|
const cron = __importStar(require("node-cron"));
|
|
@@ -60,6 +60,8 @@ const integration_credentials_1 = __importDefault(require("./middlewares/integra
|
|
|
60
60
|
const json_response_1 = __importDefault(require("./middlewares/json-response"));
|
|
61
61
|
const storage = __importStar(require("./storage"));
|
|
62
62
|
const util_1 = require("./util");
|
|
63
|
+
var models_1 = require("./models");
|
|
64
|
+
Object.defineProperty(exports, "Scope", { enumerable: true, get: function () { return models_1.Scope; } });
|
|
63
65
|
function addCrowdinEndpoints(app, config) {
|
|
64
66
|
storage.connect(config.dbFolder);
|
|
65
67
|
app.use(express_1.default.json({ limit: '50mb' }));
|
|
@@ -121,7 +123,7 @@ function addCrowdinEndpoints(app, config) {
|
|
|
121
123
|
}
|
|
122
124
|
}
|
|
123
125
|
if (config.customFileFormat) {
|
|
124
|
-
app.post('/process', (0, process_1.default)(config, config.baseUrl, config.customFileFormat.filesFolder || config.dbFolder, config.customFileFormat));
|
|
126
|
+
app.post('/process', (0, crowdin_client_1.default)(config), (0, process_1.default)(config, config.baseUrl, config.customFileFormat.filesFolder || config.dbFolder, config.customFileFormat));
|
|
125
127
|
app.get('/file/download', (0, download_1.default)(config, config.customFileFormat.filesFolder || config.dbFolder));
|
|
126
128
|
}
|
|
127
129
|
if (config.customMT) {
|
|
@@ -184,6 +186,12 @@ function addCrowdinEndpoints(app, config) {
|
|
|
184
186
|
}
|
|
185
187
|
}),
|
|
186
188
|
deleteMetadata: storage.deleteMetadata,
|
|
189
|
+
getUserSettings: (clientId) => __awaiter(this, void 0, void 0, function* () {
|
|
190
|
+
const integrationCredentials = yield storage.getIntegrationCredentials(clientId);
|
|
191
|
+
if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.config) {
|
|
192
|
+
return JSON.parse(integrationCredentials.config);
|
|
193
|
+
}
|
|
194
|
+
}),
|
|
187
195
|
establishCrowdinConnection: jwtToken => (0, crowdin_client_1.prepareCrowdinRequest)(jwtToken, config),
|
|
188
196
|
};
|
|
189
197
|
}
|
|
@@ -13,6 +13,17 @@ exports.prepareCrowdinRequest = void 0;
|
|
|
13
13
|
const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
|
|
14
14
|
const storage_1 = require("../storage");
|
|
15
15
|
const util_1 = require("../util");
|
|
16
|
+
function getToken(req) {
|
|
17
|
+
const tokenJwt = (req.query.tokenJwt || req.query.jwtToken);
|
|
18
|
+
if (tokenJwt) {
|
|
19
|
+
return tokenJwt;
|
|
20
|
+
}
|
|
21
|
+
if (req.headers.authorization) {
|
|
22
|
+
if (req.headers.authorization.startsWith('Bearer ') || req.headers.authorization.startsWith('bearer ')) {
|
|
23
|
+
return req.headers.authorization.substring(7);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
16
27
|
function prepareCrowdinRequest(jwtToken, config, optional = false) {
|
|
17
28
|
return __awaiter(this, void 0, void 0, function* () {
|
|
18
29
|
let context;
|
|
@@ -47,7 +58,7 @@ function prepareCrowdinRequest(jwtToken, config, optional = false) {
|
|
|
47
58
|
exports.prepareCrowdinRequest = prepareCrowdinRequest;
|
|
48
59
|
function handle(config, optional = false) {
|
|
49
60
|
return (0, util_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
50
|
-
const tokenJwt = (req
|
|
61
|
+
const tokenJwt = getToken(req);
|
|
51
62
|
if (!tokenJwt) {
|
|
52
63
|
return res.status(403).send({ error: 'Access denied' });
|
|
53
64
|
}
|
package/out/models/index.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export interface Config extends ImagePath {
|
|
|
14
14
|
* https url where an app is reachable from the internet (e.g. the one that ngrok generates for us)
|
|
15
15
|
*/
|
|
16
16
|
baseUrl: string;
|
|
17
|
+
/**
|
|
18
|
+
* Set of scopes requested by this app (default 'project')
|
|
19
|
+
*/
|
|
20
|
+
scopes?: Scope[];
|
|
17
21
|
/**
|
|
18
22
|
* app name
|
|
19
23
|
*/
|
|
@@ -99,6 +103,21 @@ export interface Config extends ImagePath {
|
|
|
99
103
|
*/
|
|
100
104
|
onError?: (error: any) => void;
|
|
101
105
|
}
|
|
106
|
+
export declare enum Scope {
|
|
107
|
+
ALL_SCOPES = "all",
|
|
108
|
+
NOTIFICATIONS = "notification",
|
|
109
|
+
TRANSLATION_MEMORIES = "tm",
|
|
110
|
+
MACHINE_TRANSLATION_ENGINES = "mt",
|
|
111
|
+
GLOSSARIES = "glossary",
|
|
112
|
+
PROJECTS = "project",
|
|
113
|
+
TASKS = "project.task",
|
|
114
|
+
REPORTS = "project.report",
|
|
115
|
+
TRANSLATION_STATUS = "project.status",
|
|
116
|
+
SOURCE_FILES_AND_STRINGS = "project.source",
|
|
117
|
+
WEBHOOKS = "project.webhook",
|
|
118
|
+
TRANSLATIONS = "project.translation",
|
|
119
|
+
SCREENSHOTS = "project.screenshot"
|
|
120
|
+
}
|
|
102
121
|
export interface IntegrationLogic {
|
|
103
122
|
/**
|
|
104
123
|
* Customize your app login form
|
|
@@ -363,11 +382,11 @@ export interface CustomFileFormatLogic {
|
|
|
363
382
|
/**
|
|
364
383
|
* Used for initial source file upload, source file update, and translation upload
|
|
365
384
|
*/
|
|
366
|
-
parseFile: (fileContent: string | object, req: Omit<ProcessFileRequest, 'jobType' | 'file'
|
|
385
|
+
parseFile: (fileContent: string | object, req: Omit<ProcessFileRequest, 'jobType' | 'file'>, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<ParseFileResponse>;
|
|
367
386
|
/**
|
|
368
387
|
* Used for translation download
|
|
369
388
|
*/
|
|
370
|
-
buildFile: (fileContent: string | object, req: Omit<ProcessFileRequest, 'jobType' | 'file'>, strings: ProcessFileString[]) => Promise<BuildFileResponse>;
|
|
389
|
+
buildFile: (fileContent: string | object, req: Omit<ProcessFileRequest, 'jobType' | 'file'>, strings: ProcessFileString[], client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<BuildFileResponse>;
|
|
371
390
|
}
|
|
372
391
|
export interface SignaturePatterns {
|
|
373
392
|
fileName?: string;
|
|
@@ -447,6 +466,10 @@ export interface CrowdinAppUtilities {
|
|
|
447
466
|
saveMetadata: (id: string, metadata: any) => Promise<void>;
|
|
448
467
|
getMetadata: (id: string) => Promise<any | undefined>;
|
|
449
468
|
deleteMetadata: (id: string) => Promise<void>;
|
|
469
|
+
/**
|
|
470
|
+
* Settings that users manage in the integration module
|
|
471
|
+
*/
|
|
472
|
+
getUserSettings: (clientId: string) => Promise<any | undefined>;
|
|
450
473
|
establishCrowdinConnection: (jwtToken: string) => Promise<{
|
|
451
474
|
context: CrowdinContextInfo;
|
|
452
475
|
client?: Crowdin;
|
package/out/models/index.js
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EditorPanelsMode = exports.ProcessFileJobType = exports.AccountType = void 0;
|
|
3
|
+
exports.EditorPanelsMode = exports.ProcessFileJobType = exports.AccountType = exports.Scope = void 0;
|
|
4
|
+
var Scope;
|
|
5
|
+
(function (Scope) {
|
|
6
|
+
Scope["ALL_SCOPES"] = "all";
|
|
7
|
+
Scope["NOTIFICATIONS"] = "notification";
|
|
8
|
+
Scope["TRANSLATION_MEMORIES"] = "tm";
|
|
9
|
+
Scope["MACHINE_TRANSLATION_ENGINES"] = "mt";
|
|
10
|
+
Scope["GLOSSARIES"] = "glossary";
|
|
11
|
+
Scope["PROJECTS"] = "project";
|
|
12
|
+
Scope["TASKS"] = "project.task";
|
|
13
|
+
Scope["REPORTS"] = "project.report";
|
|
14
|
+
Scope["TRANSLATION_STATUS"] = "project.status";
|
|
15
|
+
Scope["SOURCE_FILES_AND_STRINGS"] = "project.source";
|
|
16
|
+
Scope["WEBHOOKS"] = "project.webhook";
|
|
17
|
+
Scope["TRANSLATIONS"] = "project.translation";
|
|
18
|
+
Scope["SCREENSHOTS"] = "project.screenshot";
|
|
19
|
+
})(Scope = exports.Scope || (exports.Scope = {}));
|
|
4
20
|
var AccountType;
|
|
5
21
|
(function (AccountType) {
|
|
6
22
|
AccountType["NORMAL"] = "normal";
|
package/out/util/index.js
CHANGED
|
@@ -115,10 +115,7 @@ function applyDefaults(config, integration) {
|
|
|
115
115
|
integration.getCrowdinFiles = (projectId, client, rootFolder) => __awaiter(this, void 0, void 0, function* () {
|
|
116
116
|
let allDirectories;
|
|
117
117
|
if (rootFolder) {
|
|
118
|
-
allDirectories = (yield client.sourceFilesApi
|
|
119
|
-
.withFetchAll()
|
|
120
|
-
// @ts-expect-error: Method overloading
|
|
121
|
-
.listProjectDirectories(projectId, {
|
|
118
|
+
allDirectories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId, {
|
|
122
119
|
directoryId: rootFolder.id,
|
|
123
120
|
recursion: 'true',
|
|
124
121
|
})).data.map(d => d.data);
|
|
@@ -166,10 +166,10 @@
|
|
|
166
166
|
.then((res) => {
|
|
167
167
|
project = res;
|
|
168
168
|
appComponent.setCrowdinLanguagesData(project.targetLanguages)
|
|
169
|
-
{{#if withCronSync}}
|
|
170
|
-
getSyncSettings('crowdin');
|
|
171
|
-
{{/if}}
|
|
172
169
|
})
|
|
170
|
+
{{#if withCronSync}}
|
|
171
|
+
.then(() => getSyncSettings('crowdin'))
|
|
172
|
+
{{/if}}
|
|
173
173
|
.catch(e => catchRejection(e, 'Can\'t fetch Crowdin data'))
|
|
174
174
|
.finally(() => (appComponent.setAttribute('is-crowdin-loading', false)));
|
|
175
175
|
}
|
|
@@ -204,11 +204,16 @@
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
function getSyncSettings(provider) {
|
|
207
|
-
const filesComponent = appComponent.querySelector(`#${provider}-files`);
|
|
208
207
|
checkOrigin()
|
|
209
208
|
.then(restParams => fetch(`/api/sync-settings/${provider}` + restParams))
|
|
210
209
|
.then(checkResponse)
|
|
211
|
-
.then((res) =>
|
|
210
|
+
.then((res) => {
|
|
211
|
+
if (provider === 'crowdin') {
|
|
212
|
+
appComponent.setCrowdinScheduleSync(res);
|
|
213
|
+
} else {
|
|
214
|
+
appComponent.setIntegrationScheduleSync(res);
|
|
215
|
+
}
|
|
216
|
+
})
|
|
212
217
|
.catch(e => catchRejection(e, 'Can\'t fetch file progress'));
|
|
213
218
|
}
|
|
214
219
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowdin/app-project-module",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
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",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"test": "echo \"test not implemented\""
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@crowdin/crowdin-apps-functions": "0.0
|
|
15
|
+
"@crowdin/crowdin-apps-functions": "0.1.0",
|
|
16
16
|
"crypto-js": "^4.0.0",
|
|
17
17
|
"express": "4.17.1",
|
|
18
18
|
"express-handlebars": "^5.3.4",
|