@crowdin/app-project-module 2.10.0 → 2.11.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/out/index.d.ts +1 -0
- package/out/index.js +12 -7
- package/out/static/ui/form.bundle.js +1049 -220
- package/out/static/ui/form.bundle.js.map +1 -1
- package/out/storage/drizzle/index.d.ts +1 -0
- package/out/storage/drizzle/index.js +32 -0
- package/out/storage/types.d.ts +1 -0
- package/out/types.d.ts +15 -1
- package/out/types.js +1 -0
- package/out/util/connection.js +1 -4
- package/out/util/crowdin-storage.d.ts +7 -0
- package/out/util/crowdin-storage.js +78 -0
- package/package.json +3 -2
|
@@ -28,6 +28,7 @@ export declare class DrizzleStorage implements Storage {
|
|
|
28
28
|
updateIntegrationManagers(id: string, managers: string): Promise<void>;
|
|
29
29
|
getIntegrationCredentials(id: string): Promise<IntegrationCredentials | undefined>;
|
|
30
30
|
getAllIntegrationCredentials(crowdinId: string): Promise<IntegrationCredentials[]>;
|
|
31
|
+
getIntegrationCredentialsByProject(organization: string, projectId: number): Promise<IntegrationCredentials[]>;
|
|
31
32
|
deleteIntegrationCredentials(id: string): Promise<void>;
|
|
32
33
|
deleteAllIntegrationCredentials(crowdinId: string): Promise<void>;
|
|
33
34
|
saveMetadata(id: string, metadata: any, crowdinId: string): Promise<void>;
|
|
@@ -13,6 +13,7 @@ exports.DrizzleStorage = void 0;
|
|
|
13
13
|
const crypto_1 = require("crypto");
|
|
14
14
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
15
15
|
const types_1 = require("../../modules/integration/util/types");
|
|
16
|
+
const token_1 = require("../../util/app-functions/token");
|
|
16
17
|
const d1_1 = require("./d1");
|
|
17
18
|
const types_2 = require("./d1/types");
|
|
18
19
|
const postgresql_1 = require("./postgresql");
|
|
@@ -274,6 +275,37 @@ class DrizzleStorage {
|
|
|
274
275
|
}));
|
|
275
276
|
});
|
|
276
277
|
}
|
|
278
|
+
getIntegrationCredentialsByProject(organization, projectId) {
|
|
279
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
280
|
+
yield this.migrationPromise;
|
|
281
|
+
// id format: "{organization}__{projectId}__{userId}"
|
|
282
|
+
// parseCrowdinId is the authoritative parser; used as a post-filter in both paths.
|
|
283
|
+
const filterByProjectId = (item) => {
|
|
284
|
+
const { organization: itemOrganization, projectId: itemProjectId } = (0, token_1.parseCrowdinId)(item.id);
|
|
285
|
+
return itemOrganization === organization && itemProjectId === projectId;
|
|
286
|
+
};
|
|
287
|
+
// Org names with underscores make the LIKE pre-filter unreliable (SQL treats _ as a
|
|
288
|
+
// single-char wildcard), so fall back to a full org-level load + JS filter.
|
|
289
|
+
if (organization.includes('_')) {
|
|
290
|
+
const all = yield this.getAllIntegrationCredentials(organization);
|
|
291
|
+
return all.filter(filterByProjectId);
|
|
292
|
+
}
|
|
293
|
+
// LIKE narrows the DB scan to rows whose id starts with the right org and projectId prefix.
|
|
294
|
+
const res = yield this.db.query.integrationCredentials.findMany({
|
|
295
|
+
where: (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(this.schema.integrationCredentials.crowdin_id, organization), (0, drizzle_orm_1.like)(this.schema.integrationCredentials.id, `${organization}__${projectId}__%`)),
|
|
296
|
+
});
|
|
297
|
+
return (res
|
|
298
|
+
.map((item) => ({
|
|
299
|
+
id: item.id,
|
|
300
|
+
credentials: item.credentials,
|
|
301
|
+
crowdinId: item.crowdin_id,
|
|
302
|
+
managers: item.managers,
|
|
303
|
+
}))
|
|
304
|
+
// SQL LIKE treats _ as a single-char wildcard, so "org__1__%" also matches "org__10__456".
|
|
305
|
+
// parseCrowdinId gives the exact match LIKE cannot guarantee.
|
|
306
|
+
.filter(filterByProjectId));
|
|
307
|
+
});
|
|
308
|
+
}
|
|
277
309
|
deleteIntegrationCredentials(id) {
|
|
278
310
|
return __awaiter(this, void 0, void 0, function* () {
|
|
279
311
|
yield this.migrationPromise;
|
package/out/storage/types.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface Storage {
|
|
|
28
28
|
updateIntegrationManagers(id: string, managers: string): Promise<void>;
|
|
29
29
|
getIntegrationCredentials(id: string): Promise<IntegrationCredentials | undefined>;
|
|
30
30
|
getAllIntegrationCredentials(crowdinId: string): Promise<IntegrationCredentials[]>;
|
|
31
|
+
getIntegrationCredentialsByProject(organization: string, projectId: number): Promise<IntegrationCredentials[]>;
|
|
31
32
|
deleteIntegrationCredentials(id: string): Promise<void>;
|
|
32
33
|
deleteAllIntegrationCredentials(crowdinId: string): Promise<void>;
|
|
33
34
|
saveMetadata(id: string, metadata: any, crowdinId: string): Promise<void>;
|
package/out/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Crowdin from '@crowdin/crowdin-api-client';
|
|
2
|
+
import type { AppStorage } from '@crowdin/apps-storage';
|
|
2
3
|
import { Request } from 'express';
|
|
3
4
|
import { ContextContent } from './modules/context-menu/types';
|
|
4
5
|
import { CustomMTLogic } from './modules/custom-mt/types';
|
|
@@ -393,7 +394,8 @@ export declare enum Scope {
|
|
|
393
394
|
AI_PROVIDERS = "ai.provider",
|
|
394
395
|
AI_PROMPTS = "ai.prompt",
|
|
395
396
|
AI_PROXIES = "ai.proxy",
|
|
396
|
-
APPLICATIONS = "application"
|
|
397
|
+
APPLICATIONS = "application",
|
|
398
|
+
APPLICATION_STORAGE = "application.storage"
|
|
397
399
|
}
|
|
398
400
|
export interface CrowdinClientRequest extends Request {
|
|
399
401
|
crowdinApiClient: Crowdin;
|
|
@@ -478,6 +480,18 @@ export interface CrowdinAppUtilities extends CrowdinMetadataStore {
|
|
|
478
480
|
}) => (req: any, res: any, next: any) => void;
|
|
479
481
|
storage: Storage;
|
|
480
482
|
cron: Cron;
|
|
483
|
+
/**
|
|
484
|
+
* Crowdin-hosted storage for this app installation (the `kv` key-value space).
|
|
485
|
+
* Unlike `storage` (the app's own database), this data lives on the Crowdin side
|
|
486
|
+
* and is shared across the installation. Requires the `crowdin_app` authentication
|
|
487
|
+
* type (the default) - the storage endpoints accept only the application's own
|
|
488
|
+
* token, so every other authentication type is rejected. `kv.user.*` records are
|
|
489
|
+
* private to the identity behind the stored credentials.
|
|
490
|
+
* Optionally enumerate the app's module keys in the type parameter
|
|
491
|
+
* (`crowdinStorage<'main' | 'reports'>(...)`) to get the full
|
|
492
|
+
* `module:{moduleKey}:` prefixes suggested in key positions.
|
|
493
|
+
*/
|
|
494
|
+
crowdinStorage: <M extends string = never>(crowdinId: string) => Promise<AppStorage<M>>;
|
|
481
495
|
}
|
|
482
496
|
export interface CrowdinMetadataStore {
|
|
483
497
|
saveMetadata: (options: {
|
package/out/types.js
CHANGED
|
@@ -36,6 +36,7 @@ var Scope;
|
|
|
36
36
|
Scope["AI_PROMPTS"] = "ai.prompt";
|
|
37
37
|
Scope["AI_PROXIES"] = "ai.proxy";
|
|
38
38
|
Scope["APPLICATIONS"] = "application";
|
|
39
|
+
Scope["APPLICATION_STORAGE"] = "application.storage";
|
|
39
40
|
})(Scope || (exports.Scope = Scope = {}));
|
|
40
41
|
var AccountType;
|
|
41
42
|
(function (AccountType) {
|
package/out/util/connection.js
CHANGED
|
@@ -266,10 +266,7 @@ function resolveIntegrationAccess(clientId) {
|
|
|
266
266
|
const ownerIds = [];
|
|
267
267
|
let projectIntegrationCredentials = [];
|
|
268
268
|
if (!integrationCredentials) {
|
|
269
|
-
projectIntegrationCredentials =
|
|
270
|
-
const { organization: itemOrganization, projectId: itemProjectId } = (0, token_1.parseCrowdinId)(item.id);
|
|
271
|
-
return itemOrganization === organization && itemProjectId === projectId;
|
|
272
|
-
});
|
|
269
|
+
projectIntegrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentialsByProject(organization, projectId);
|
|
273
270
|
for (const credentials of projectIntegrationCredentials) {
|
|
274
271
|
ownerIds.push((0, token_1.parseCrowdinId)(credentials.id).userId);
|
|
275
272
|
if (credentials.managers) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AppStorage, type StorageTransport } from '@crowdin/apps-storage';
|
|
2
|
+
import { Config } from '../types';
|
|
3
|
+
export declare function createCrowdinStorageTransport({ config, crowdinId, }: {
|
|
4
|
+
config: Config;
|
|
5
|
+
crowdinId: string;
|
|
6
|
+
}): StorageTransport;
|
|
7
|
+
export declare function createCrowdinStorage<M extends string = never>(config: Config, crowdinId: string): Promise<AppStorage<M>>;
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.createCrowdinStorageTransport = createCrowdinStorageTransport;
|
|
16
|
+
exports.createCrowdinStorage = createCrowdinStorage;
|
|
17
|
+
const axios_1 = __importDefault(require("axios"));
|
|
18
|
+
const apps_storage_1 = require("@crowdin/apps-storage");
|
|
19
|
+
const storage_1 = require("../storage");
|
|
20
|
+
const types_1 = require("../types");
|
|
21
|
+
const connection_1 = require("./connection");
|
|
22
|
+
function loadCredentials(crowdinId) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const credentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(crowdinId);
|
|
25
|
+
if (!credentials) {
|
|
26
|
+
throw new Error('Failed to find Crowdin credentials');
|
|
27
|
+
}
|
|
28
|
+
return credentials;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function createCrowdinStorageTransport({ config, crowdinId, }) {
|
|
32
|
+
const send = (request, token, apiUrl) => __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
const url = `${apiUrl}/applications/${encodeURIComponent(config.identifier)}/storage/${request.path}`;
|
|
34
|
+
return axios_1.default.request({
|
|
35
|
+
method: request.method,
|
|
36
|
+
url,
|
|
37
|
+
params: request.query,
|
|
38
|
+
data: request.body,
|
|
39
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
40
|
+
validateStatus: () => true,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
return (request) => __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
const initial = yield (0, connection_1.prepareCrowdinClient)({
|
|
45
|
+
config,
|
|
46
|
+
credentials: yield loadCredentials(crowdinId),
|
|
47
|
+
autoRenew: true,
|
|
48
|
+
});
|
|
49
|
+
let response = yield send(request, initial.token, initial.client.url);
|
|
50
|
+
if (response.status === 401) {
|
|
51
|
+
const refreshed = yield (0, connection_1.prepareCrowdinClient)({
|
|
52
|
+
config,
|
|
53
|
+
credentials: yield loadCredentials(crowdinId),
|
|
54
|
+
autoRenew: true,
|
|
55
|
+
});
|
|
56
|
+
response = yield send(request, refreshed.token, refreshed.client.url);
|
|
57
|
+
}
|
|
58
|
+
return { status: response.status, body: response.data };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function createCrowdinStorage(config, crowdinId) {
|
|
62
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
+
const authenticationType = config.authenticationType || types_1.AuthenticationType.APP;
|
|
64
|
+
if (authenticationType !== types_1.AuthenticationType.APP) {
|
|
65
|
+
throw new Error(`Crowdin storage is not available for ${authenticationType} authentication because the storage endpoints accept only the application's own token`);
|
|
66
|
+
}
|
|
67
|
+
yield loadCredentials(crowdinId);
|
|
68
|
+
return (0, apps_storage_1.createStorage)({
|
|
69
|
+
transport: createCrowdinStorageTransport({ config, crowdinId }),
|
|
70
|
+
getUserId: () => __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const credentials = yield loadCredentials(crowdinId);
|
|
72
|
+
return credentials.userId !== null && credentials.userId !== undefined
|
|
73
|
+
? String(credentials.userId)
|
|
74
|
+
: undefined;
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowdin/app-project-module",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.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",
|
|
@@ -57,8 +57,9 @@
|
|
|
57
57
|
"out/**/*"
|
|
58
58
|
],
|
|
59
59
|
"dependencies": {
|
|
60
|
+
"@crowdin/apps-storage": "0.x",
|
|
60
61
|
"@crowdin/crowdin-api-client": "^1.56.1",
|
|
61
|
-
"@crowdin/logs-formatter": "^3.
|
|
62
|
+
"@crowdin/logs-formatter": "^3.1.0",
|
|
62
63
|
"@godaddy/terminus": "^4.12.1",
|
|
63
64
|
"ajv": "^8.20.0",
|
|
64
65
|
"amqplib": "^2.0.1",
|