@crowdin/app-project-module 0.15.5 → 0.16.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 CHANGED
@@ -186,7 +186,8 @@ configuration.pricing = {
186
186
  trial: 14, //amount of days to use app for free
187
187
  trialCrowdin: 14, //amount of days specifically in crowdin workspace
188
188
  trialEnterprise: 30, //amount of days specifically for enterprise
189
- cachingSeconds: 12 * 60 * 60 //time in seconds of how long to cache subscription info
189
+ cachingSeconds: 12 * 60 * 60, //time in seconds of how long to cache subscription info
190
+ infoDisplayDaysThreshold: 14 //number of days threshold to check if subscription info should be displayed (if not defined then info will be always visible)
190
191
  };
191
192
  ```
192
193
 
@@ -10,6 +10,7 @@ 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 connection_1 = require("../util/connection");
13
14
  const defaults_1 = require("../util/defaults");
14
15
  function constructOauthUrl(config, integration) {
15
16
  var _a, _b, _c;
@@ -55,6 +56,7 @@ function handle(config, integration) {
55
56
  options.infoModal = integration.infoModal;
56
57
  options.withCronSync = integration.withCronSync;
57
58
  options.withWebhookSync = integration.withWebhookSync;
59
+ options.checkSubscription = !(0, connection_1.isAppFree)(config);
58
60
  (0, util_1.log)(`Routing user to ${view} view`, config.logger);
59
61
  return res.render(view, options);
60
62
  }), config.onError);
@@ -0,0 +1,3 @@
1
+ import { Request, Response } from 'express';
2
+ import { Config } from '../models';
3
+ export default function handle(config: Config): (req: Request, res: Response) => void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function handle(config) {
4
+ return (req, res) => {
5
+ var _a;
6
+ const subscriptionInfo = req.subscriptionInfo;
7
+ let showInfo = true;
8
+ if ((_a = config.pricing) === null || _a === void 0 ? void 0 : _a.infoDisplayDaysThreshold) {
9
+ showInfo = config.pricing.infoDisplayDaysThreshold >= ((subscriptionInfo === null || subscriptionInfo === void 0 ? void 0 : subscriptionInfo.daysLeft) || 0);
10
+ }
11
+ res.send(Object.assign(Object.assign({}, (subscriptionInfo || {})), { showInfo }));
12
+ };
13
+ }
14
+ exports.default = handle;
package/out/index.js CHANGED
@@ -52,6 +52,7 @@ const main_1 = __importDefault(require("./handlers/main"));
52
52
  const manifest_1 = __importDefault(require("./handlers/manifest"));
53
53
  const oauth_login_1 = __importDefault(require("./handlers/oauth-login"));
54
54
  const settings_save_1 = __importDefault(require("./handlers/settings-save"));
55
+ const subscription_info_1 = __importDefault(require("./handlers/subscription-info"));
55
56
  const sync_settings_1 = __importDefault(require("./handlers/sync-settings"));
56
57
  const sync_settings_save_1 = __importDefault(require("./handlers/sync-settings-save"));
57
58
  const uninstall_1 = __importDefault(require("./handlers/uninstall"));
@@ -97,6 +98,7 @@ function addCrowdinEndpoints(app, config) {
97
98
  (0, defaults_1.applyDefaults)(config, integrationLogic);
98
99
  app.get('/logo/integration/logo.png', (req, res) => res.sendFile(integrationLogic.imagePath || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')));
99
100
  app.get('/', (0, crowdin_client_1.default)(config, true, false), (0, integration_credentials_1.default)(config, integrationLogic, true), (0, main_1.default)(config, integrationLogic));
101
+ app.get('/api/subscription-info', json_response_1.default, (0, crowdin_client_1.default)(config), (0, subscription_info_1.default)(config));
100
102
  app.post('/api/settings', (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, settings_save_1.default)(config));
101
103
  app.post('/api/login', (0, crowdin_client_1.default)(config, false, false), (0, integration_login_1.default)(config, integrationLogic));
102
104
  app.post('/api/logout', (0, crowdin_client_1.default)(config, false, false), (0, integration_logout_1.default)(config));
@@ -1,9 +1,10 @@
1
1
  /// <reference types="qs" />
2
2
  import Crowdin from '@crowdin/crowdin-api-client';
3
3
  import { Response } from 'express';
4
- import { Config, CrowdinContextInfo } from '../models';
4
+ import { Config, CrowdinContextInfo, SubscriptionInfo } from '../models';
5
5
  export declare function prepareCrowdinRequest(jwtToken: string, config: Config, optional?: boolean, checkSubscriptionExpiration?: boolean): Promise<{
6
6
  context: CrowdinContextInfo;
7
7
  client?: Crowdin;
8
+ subscriptionInfo?: SubscriptionInfo;
8
9
  }>;
9
10
  export default function handle(config: Config, optional?: boolean, checkSubscriptionExpiration?: boolean): (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;
@@ -44,13 +44,14 @@ function prepareCrowdinRequest(jwtToken, config, optional = false, checkSubscrip
44
44
  }
45
45
  (0, util_1.log)('Building crowdin client instance', config.logger);
46
46
  const { client, token } = yield (0, connection_1.prepareCrowdinClient)(config, credentials);
47
+ let subscriptionInfo;
47
48
  if (checkSubscriptionExpiration) {
48
- const { expired, subscribeLink } = yield (0, connection_1.checkSubscription)(config, token, credentials.id, credentials.type);
49
- if (expired) {
50
- throw new util_1.CodeError(subscribeLink || '', 402);
49
+ subscriptionInfo = yield (0, connection_1.checkSubscription)(config, token, credentials.id, credentials.type);
50
+ if (subscriptionInfo.expired) {
51
+ throw new util_1.CodeError(subscriptionInfo.subscribeLink || '', 402);
51
52
  }
52
53
  }
53
- return { context, client };
54
+ return { context, client, subscriptionInfo };
54
55
  });
55
56
  }
56
57
  exports.prepareCrowdinRequest = prepareCrowdinRequest;
@@ -66,6 +67,7 @@ function handle(config, optional = false, checkSubscriptionExpiration = true) {
66
67
  if (data.client) {
67
68
  req.crowdinApiClient = data.client;
68
69
  }
70
+ req.subscriptionInfo = data.subscriptionInfo;
69
71
  next();
70
72
  }
71
73
  catch (e) {
@@ -322,6 +322,7 @@ export interface IntegrationRequest extends CrowdinClientRequest {
322
322
  export interface CrowdinClientRequest extends Request {
323
323
  crowdinApiClient: Crowdin;
324
324
  crowdinContext: CrowdinContextInfo;
325
+ subscriptionInfo?: SubscriptionInfo;
325
326
  }
326
327
  export interface CrowdinCredentials {
327
328
  id: string;
@@ -339,6 +340,11 @@ export interface CrowdinContextInfo {
339
340
  crowdinId: string;
340
341
  clientId: string;
341
342
  }
343
+ export interface SubscriptionInfo {
344
+ expired: boolean;
345
+ subscribeLink?: string;
346
+ daysLeft?: number;
347
+ }
342
348
  export interface IntegrationCredentials {
343
349
  id: string;
344
350
  credentials: any;
@@ -498,5 +504,6 @@ export interface Pricing {
498
504
  trialCrowdin?: number;
499
505
  trialEnterprise?: number;
500
506
  cachingSeconds?: number;
507
+ infoDisplayDaysThreshold?: number;
501
508
  }
502
509
  export {};
@@ -12,6 +12,12 @@
12
12
  justify-content: space-around;
13
13
  }
14
14
 
15
+ .box-center {
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ }
20
+
15
21
  .top {
16
22
  text-align: right;
17
23
  margin-bottom: 10px;
@@ -41,3 +47,11 @@
41
47
  .login crowdin-h4 {
42
48
  margin: 8px 0 16px;
43
49
  }
50
+
51
+ .ml-1 {
52
+ margin-left: 8px;
53
+ }
54
+
55
+ .m-0 {
56
+ margin: 0;
57
+ }
@@ -1,12 +1,10 @@
1
1
  import Crowdin from '@crowdin/crowdin-api-client';
2
- import { AccountType, Config, CrowdinCredentials, IntegrationCredentials, IntegrationLogic } from '../models';
2
+ import { AccountType, Config, CrowdinCredentials, IntegrationCredentials, IntegrationLogic, SubscriptionInfo } from '../models';
3
3
  export declare function prepareCrowdinClient(config: Config, credentials: CrowdinCredentials): Promise<{
4
4
  client: Crowdin;
5
5
  token: string;
6
6
  }>;
7
7
  export declare function prepareIntegrationCredentials(config: Config, integration: IntegrationLogic, integrationCredentials: IntegrationCredentials): Promise<any>;
8
8
  export declare function clearCache(organization: string): void;
9
- export declare function checkSubscription(config: Config, token: string, organization: string, accountType: AccountType): Promise<{
10
- expired: boolean;
11
- subscribeLink?: string;
12
- }>;
9
+ export declare function isAppFree(config: Config): boolean;
10
+ export declare function checkSubscription(config: Config, token: string, organization: string, accountType: AccountType): Promise<SubscriptionInfo>;
@@ -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.checkSubscription = exports.clearCache = exports.prepareIntegrationCredentials = exports.prepareCrowdinClient = void 0;
34
+ exports.checkSubscription = exports.isAppFree = exports.clearCache = exports.prepareIntegrationCredentials = exports.prepareCrowdinClient = void 0;
35
35
  const crowdin_api_client_1 = __importDefault(require("@crowdin/crowdin-api-client"));
36
36
  const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
37
37
  const axios_1 = __importDefault(require("axios"));
@@ -133,19 +133,29 @@ function clearCache(organization) {
133
133
  delete subscriptionCache[organization];
134
134
  }
135
135
  exports.clearCache = clearCache;
136
+ function isAppFree(config) {
137
+ return !config.pricing || config.pricing.planType === 'free';
138
+ }
139
+ exports.isAppFree = isAppFree;
140
+ function validateSubscription(date) {
141
+ const expired = date.getTime() < Date.now();
142
+ const daysLeft = Math.round((date.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
143
+ return { expired, daysLeft };
144
+ }
136
145
  function checkSubscription(config, token, organization, accountType) {
146
+ var _a, _b, _c, _d, _e, _f;
137
147
  return __awaiter(this, void 0, void 0, function* () {
138
- if (!config.pricing || config.pricing.planType === 'free') {
148
+ if (isAppFree(config)) {
139
149
  return { expired: false };
140
150
  }
141
151
  //default 2 weeks
142
152
  const defaultSubscriptionPlan = 14;
143
153
  let days;
144
154
  if (organization) {
145
- days = config.pricing.trialEnterprise || config.pricing.trial || defaultSubscriptionPlan;
155
+ days = ((_a = config.pricing) === null || _a === void 0 ? void 0 : _a.trialEnterprise) || ((_b = config.pricing) === null || _b === void 0 ? void 0 : _b.trial) || defaultSubscriptionPlan;
146
156
  }
147
157
  else {
148
- days = config.pricing.trialCrowdin || config.pricing.trial || defaultSubscriptionPlan;
158
+ days = ((_c = config.pricing) === null || _c === void 0 ? void 0 : _c.trialCrowdin) || ((_d = config.pricing) === null || _d === void 0 ? void 0 : _d.trial) || defaultSubscriptionPlan;
149
159
  }
150
160
  (0, _1.log)(`Checking subscription plan. Subscriptino plan ${days} days`, config.logger);
151
161
  const appIdentifier = config.identifier;
@@ -154,9 +164,9 @@ function checkSubscription(config, token, organization, accountType) {
154
164
  const { cacheValidUntil, validUntil, subscribeLink } = cacheEntry;
155
165
  if (cacheValidUntil.getTime() > Date.now()) {
156
166
  (0, _1.log)(`Loaded data from cache. Subscription is vali until ${validUntil.toISOString()}`, config.logger);
157
- const expired = new Date(validUntil).getTime() < Date.now();
167
+ const { expired, daysLeft } = validateSubscription(new Date(validUntil));
158
168
  (0, _1.log)(`expired ${expired}`, config.logger);
159
- return { expired, subscribeLink };
169
+ return { expired, subscribeLink, daysLeft };
160
170
  }
161
171
  }
162
172
  try {
@@ -166,10 +176,10 @@ function checkSubscription(config, token, organization, accountType) {
166
176
  token,
167
177
  });
168
178
  (0, _1.log)(`Recieved subscription info. ${JSON.stringify(subscription)}`, config.logger);
169
- const expired = new Date(subscription.expires).getTime() < Date.now();
179
+ const { expired, daysLeft } = validateSubscription(new Date(subscription.expires));
170
180
  (0, _1.log)(`expired ${expired}`, config.logger);
171
- addToCache(organization, appIdentifier, new Date(subscription.expires), config.pricing.cachingSeconds);
172
- return { expired };
181
+ addToCache(organization, appIdentifier, new Date(subscription.expires), (_e = config.pricing) === null || _e === void 0 ? void 0 : _e.cachingSeconds);
182
+ return { expired, daysLeft };
173
183
  }
174
184
  catch (e) {
175
185
  if (e instanceof crowdinAppFunctions.PaymentRequiredError) {
@@ -177,10 +187,10 @@ function checkSubscription(config, token, organization, accountType) {
177
187
  (0, _1.log)(`Recieved 402 payment error. initializedAt ${initializedAt}`, config.logger);
178
188
  const date = new Date(initializedAt);
179
189
  date.setDate(date.getDate() + days);
180
- const expired = date.getTime() < Date.now();
190
+ const { expired, daysLeft } = validateSubscription(date);
181
191
  (0, _1.log)(`expired ${expired}`, config.logger);
182
- addToCache(organization, appIdentifier, new Date(date), config.pricing.cachingSeconds, subscribeLink);
183
- return { expired, subscribeLink };
192
+ addToCache(organization, appIdentifier, new Date(date), (_f = config.pricing) === null || _f === void 0 ? void 0 : _f.cachingSeconds, subscribeLink);
193
+ return { expired, subscribeLink, daysLeft };
184
194
  }
185
195
  if (config.onError) {
186
196
  config.onError(e);
@@ -4,7 +4,15 @@
4
4
 
5
5
  <body>
6
6
  <div class="i_w">
7
- <div class='top'>
7
+ <div class="top">
8
+ {{#if checkSubscription}}
9
+ <crowdin-alert id="subscription-info" no-icon="true" type="warning" style="display: none;">
10
+ <div class="box-center">
11
+ <p class="m-0"></p>
12
+ <crowdin-button class="ml-1" primary onclick="window.open(subscriptionLink,'_blank')">Subscribe</crowdin-button>
13
+ </div>
14
+ </crowdin-alert>
15
+ {{/if}}
8
16
  {{#if infoModal}}
9
17
  <crowdin-button icon-before="info" onclick="infoModal.open();">{{infoModal.title}}</crowdin-button>
10
18
  {{/if}}
@@ -439,6 +447,25 @@
439
447
  .finally(() => (appComponent.setAttribute(`is-${provider}-loading`, false)));
440
448
  }
441
449
  {{/if}}
450
+
451
+ {{#if checkSubscription}}
452
+ const subscriptionInfo = document.getElementById('subscription-info');
453
+ const subscriptionInfoText = subscriptionInfo.getElementsByTagName('p')[0];
454
+ function getSubscriptionInfo() {
455
+ checkOrigin()
456
+ .then(restParams => fetch('api/subscription-info' + restParams))
457
+ .then(checkResponse)
458
+ .then((res) => {
459
+ if (res.showInfo) {
460
+ subscriptionLink = res.subscribeLink;
461
+ subscriptionInfoText.textContent = `Your trial expires in ${res.daysLeft} days.`
462
+ subscriptionInfo.style.display = 'block';
463
+ }
464
+ });
465
+ }
466
+
467
+ getSubscriptionInfo();
468
+ {{/if}}
442
469
  </script>
443
470
 
444
471
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.15.5",
3
+ "version": "0.16.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",