@mytmpvpn/mytmpvpn-common 4.0.1 → 5.0.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.
@@ -1,3 +1,18 @@
1
1
  export declare class MyTmpVpnError extends Error {
2
2
  constructor(msg: string);
3
3
  }
4
+ export declare class InvalidUserConfigError extends MyTmpVpnError {
5
+ constructor(msg: string);
6
+ }
7
+ export declare class InvalidVpnConfigError extends MyTmpVpnError {
8
+ constructor(msg: string);
9
+ }
10
+ export declare class MinPeanutsError extends InvalidVpnConfigError {
11
+ constructor(given: number, balance: number);
12
+ }
13
+ export declare class MaxPeanutsError extends InvalidVpnConfigError {
14
+ constructor(given: number, balance: number);
15
+ }
16
+ export declare class NotEnoughPeanutsError extends InvalidVpnConfigError {
17
+ constructor(given: number, balance: number);
18
+ }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MyTmpVpnError = void 0;
3
+ exports.NotEnoughPeanutsError = exports.MaxPeanutsError = exports.MinPeanutsError = exports.InvalidVpnConfigError = exports.InvalidUserConfigError = exports.MyTmpVpnError = void 0;
4
+ const peanuts_1 = require("./models/peanuts");
4
5
  class MyTmpVpnError extends Error {
5
6
  constructor(msg) {
6
7
  super(msg); // 'Error' breaks prototype chain here
@@ -9,3 +10,43 @@ class MyTmpVpnError extends Error {
9
10
  }
10
11
  }
11
12
  exports.MyTmpVpnError = MyTmpVpnError;
13
+ class InvalidUserConfigError extends MyTmpVpnError {
14
+ constructor(msg) {
15
+ super(msg);
16
+ // Set the prototype explicitly.
17
+ Object.setPrototypeOf(this, InvalidUserConfigError.prototype);
18
+ }
19
+ }
20
+ exports.InvalidUserConfigError = InvalidUserConfigError;
21
+ class InvalidVpnConfigError extends MyTmpVpnError {
22
+ constructor(msg) {
23
+ super(msg);
24
+ // Set the prototype explicitly.
25
+ Object.setPrototypeOf(this, InvalidVpnConfigError.prototype);
26
+ }
27
+ }
28
+ exports.InvalidVpnConfigError = InvalidVpnConfigError;
29
+ class MinPeanutsError extends InvalidVpnConfigError {
30
+ constructor(given, balance) {
31
+ super(`Minimum number of peanuts should be ${peanuts_1.PEANUTS_CONFIG.min}, you specified: ${(0, peanuts_1.peanutsToClient)(given)} and you have ${(0, peanuts_1.peanutsToClient)(balance)}.`);
32
+ // Set the prototype explicitly.
33
+ Object.setPrototypeOf(this, MinPeanutsError.prototype);
34
+ }
35
+ }
36
+ exports.MinPeanutsError = MinPeanutsError;
37
+ class MaxPeanutsError extends InvalidVpnConfigError {
38
+ constructor(given, balance) {
39
+ super(`Maximum number of peanuts should be ${peanuts_1.PEANUTS_CONFIG.max}, you specified: ${(0, peanuts_1.peanutsToClient)(given)} and you have ${(0, peanuts_1.peanutsToClient)(balance)}.`);
40
+ // Set the prototype explicitly.
41
+ Object.setPrototypeOf(this, MaxPeanutsError.prototype);
42
+ }
43
+ }
44
+ exports.MaxPeanutsError = MaxPeanutsError;
45
+ class NotEnoughPeanutsError extends InvalidVpnConfigError {
46
+ constructor(given, balance) {
47
+ super(`Not enough peanuts, you specified: ${(0, peanuts_1.peanutsToClient)(given)} and you have ${(0, peanuts_1.peanutsToClient)(balance)}.`);
48
+ // Set the prototype explicitly.
49
+ Object.setPrototypeOf(this, NotEnoughPeanutsError.prototype);
50
+ }
51
+ }
52
+ exports.NotEnoughPeanutsError = NotEnoughPeanutsError;
@@ -10,4 +10,5 @@ interface PeanutsPack {
10
10
  price: number;
11
11
  description: string[];
12
12
  }
13
- export { PEANUTS_CONFIG, PeanutsPack };
13
+ declare function peanutsToClient(peanuts: number): number;
14
+ export { PEANUTS_CONFIG, PeanutsPack, peanutsToClient };
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PEANUTS_CONFIG = void 0;
3
+ exports.peanutsToClient = exports.PEANUTS_CONFIG = void 0;
4
4
  const PEANUTS_CONFIG = {
5
5
  min: 1,
6
6
  max: 1000
7
7
  };
8
8
  exports.PEANUTS_CONFIG = PEANUTS_CONFIG;
9
+ function peanutsToClient(peanuts) {
10
+ return Number.parseFloat(peanuts.toFixed(2));
11
+ }
12
+ exports.peanutsToClient = peanutsToClient;
@@ -1,4 +1,5 @@
1
- declare enum VpnState {
1
+ import { UserVpn } from "./uservpn";
2
+ export declare enum VpnState {
2
3
  Failed = "Failed",
3
4
  Creating = "Creating",
4
5
  Created = "Created",
@@ -8,32 +9,36 @@ declare enum VpnState {
8
9
  Deprovisioning = "Deprovisioning",
9
10
  Deleted = "Deleted"
10
11
  }
11
- declare function rank(state: VpnState): number;
12
- declare enum VpnType {
12
+ export declare function toRank(state: VpnState): number;
13
+ export declare function fromRank(rank: number): VpnState;
14
+ export declare enum VpnType {
13
15
  WireGuard = "wireguard"
14
16
  }
15
- declare const MIN_DELETE_AFTER: number;
16
- declare const MAX_DELETE_AFTER: number;
17
- interface VpnConfig {
17
+ export declare const MIN_DELETE_AFTER: number;
18
+ export declare const MAX_DELETE_AFTER: number;
19
+ export interface VpnConfig {
18
20
  type: VpnType;
19
21
  maxPeanuts: number;
20
22
  deleteAfter?: number;
21
23
  }
22
- interface VpnMetrics {
24
+ export interface VpnMetrics {
23
25
  duration: number;
24
26
  bytes: number;
25
27
  peanuts: number;
26
28
  }
27
- interface Vpn {
29
+ export interface Vpn {
28
30
  vpnId: string;
29
31
  createdAt: Date;
30
32
  region: string;
31
33
  config: VpnConfig;
32
34
  state: VpnState;
33
35
  }
34
- declare function getVpnConfigTypes(): VpnType[];
35
- declare function throwIfNotValidDeleteAfter(deleteAfter: number | undefined): void;
36
- declare function newVpn(region: string, config: VpnConfig, state: VpnState): Vpn;
37
- declare function getVpnFrom(vpnId: string, state: VpnState, config: VpnConfig): Vpn;
38
- declare function vpnIdToWgFileName(vpnId: string): string;
39
- export { VpnState, type VpnMetrics, VpnType, type VpnConfig, type Vpn, rank, newVpn, getVpnFrom, getVpnConfigTypes, vpnIdToWgFileName, MIN_DELETE_AFTER, MAX_DELETE_AFTER, throwIfNotValidDeleteAfter };
36
+ export declare function getVpnConfigTypes(): VpnType[];
37
+ export declare function vpnAgainstQuotaPredicate(uservpn: UserVpn): boolean;
38
+ export declare function validateVpnNbAgainstQuota(vpnNb: number, quota: number): number;
39
+ export declare function checkValidConfig(userId: string, currentBalance: number, vpnConfig: any): VpnConfig;
40
+ export declare function newVpn(region: string, config: VpnConfig, state: VpnState): Vpn;
41
+ export declare function getVpnFrom(vpnId: string, state: VpnState, config: VpnConfig): Vpn;
42
+ export declare function vpnIdToWgFileName(vpnId: string): string;
43
+ export declare function vpnToClient(vpn: Vpn): Vpn;
44
+ export declare function metricsToClient(metrics: VpnMetrics | undefined): VpnMetrics;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.throwIfNotValidDeleteAfter = exports.MAX_DELETE_AFTER = exports.MIN_DELETE_AFTER = exports.vpnIdToWgFileName = exports.getVpnConfigTypes = exports.getVpnFrom = exports.newVpn = exports.rank = exports.VpnType = exports.VpnState = void 0;
3
+ exports.metricsToClient = exports.vpnToClient = exports.vpnIdToWgFileName = exports.getVpnFrom = exports.newVpn = exports.checkValidConfig = exports.validateVpnNbAgainstQuota = exports.vpnAgainstQuotaPredicate = exports.getVpnConfigTypes = exports.MAX_DELETE_AFTER = exports.MIN_DELETE_AFTER = exports.VpnType = exports.fromRank = exports.toRank = exports.VpnState = void 0;
4
4
  const errors_1 = require("../errors");
5
+ const peanuts_1 = require("./peanuts");
5
6
  var VpnState;
6
7
  (function (VpnState) {
7
8
  VpnState["Failed"] = "Failed";
@@ -12,52 +13,120 @@ var VpnState;
12
13
  VpnState["Running"] = "Running";
13
14
  VpnState["Deprovisioning"] = "Deprovisioning";
14
15
  VpnState["Deleted"] = "Deleted";
15
- })(VpnState || (VpnState = {}));
16
- exports.VpnState = VpnState;
16
+ })(VpnState = exports.VpnState || (exports.VpnState = {}));
17
17
  const vpnStateRank = Object.values(VpnState);
18
- function rank(state) {
18
+ function toRank(state) {
19
19
  return vpnStateRank.indexOf(state);
20
20
  }
21
- exports.rank = rank;
21
+ exports.toRank = toRank;
22
+ function fromRank(rank) {
23
+ return vpnStateRank[rank];
24
+ }
25
+ exports.fromRank = fromRank;
22
26
  var VpnType;
23
27
  (function (VpnType) {
24
28
  VpnType["WireGuard"] = "wireguard";
25
- })(VpnType || (VpnType = {}));
26
- exports.VpnType = VpnType;
27
- const MIN_DELETE_AFTER = 5 * 60; // Minimum is 5 minutes
28
- exports.MIN_DELETE_AFTER = MIN_DELETE_AFTER;
29
- const MAX_DELETE_AFTER = 24 * 60 * 60; // Maximum is 24 hours
30
- exports.MAX_DELETE_AFTER = MAX_DELETE_AFTER;
29
+ })(VpnType = exports.VpnType || (exports.VpnType = {}));
30
+ exports.MIN_DELETE_AFTER = 5 * 60; // Minimum is 5 minutes
31
+ exports.MAX_DELETE_AFTER = 24 * 60 * 60; // Maximum is 24 hours
31
32
  function getVpnConfigTypes() {
32
33
  return Object.values(VpnType).filter((item) => {
33
34
  return isNaN(Number(item));
34
35
  });
35
36
  }
36
37
  exports.getVpnConfigTypes = getVpnConfigTypes;
37
- function throwIfNotValidVpnId(vpnId) {
38
+ function validateVpnId(vpnId) {
38
39
  // e.g: YYYYMMDDHHmmssmss@az-test-7
39
40
  if (!/\d{4}[01]\d[0-3]\d[0-2]\d[0-5]\d[0-5]\d\d{3}@[a-z]{2}-[a-z]+-\d/.test(vpnId)) {
40
41
  throw new errors_1.MyTmpVpnError(`Incorrect vpnId: \'${vpnId}\'`);
41
42
  }
43
+ return vpnId;
44
+ }
45
+ function validateMaxPeanuts(peanuts, currentBalance) {
46
+ let result = peanuts;
47
+ if (peanuts <= 0) {
48
+ result = Math.min(currentBalance, peanuts_1.PEANUTS_CONFIG.max);
49
+ }
50
+ if (result > currentBalance) {
51
+ throw new errors_1.NotEnoughPeanutsError(result, currentBalance);
52
+ }
53
+ if (result < peanuts_1.PEANUTS_CONFIG.min) {
54
+ throw new errors_1.MinPeanutsError(result, currentBalance);
55
+ }
56
+ if (result > peanuts_1.PEANUTS_CONFIG.max) {
57
+ throw new errors_1.MaxPeanutsError(result, currentBalance);
58
+ }
59
+ return result;
42
60
  }
43
- function throwIfNotValidDeleteAfter(deleteAfter) {
61
+ function validateDeleteAfter(deleteAfter) {
44
62
  if (deleteAfter === undefined) {
45
- return;
63
+ return undefined;
46
64
  }
47
65
  if (isNaN(deleteAfter)) {
48
- return;
66
+ return undefined;
49
67
  }
50
68
  if (!isFinite(deleteAfter)) {
51
- throw new errors_1.MyTmpVpnError(`deleteAfter must be finite: ${deleteAfter}`);
69
+ throw new errors_1.InvalidVpnConfigError(`deleteAfter must be finite: ${deleteAfter}`);
70
+ }
71
+ if (deleteAfter < exports.MIN_DELETE_AFTER) {
72
+ throw new errors_1.InvalidVpnConfigError(`deleteAfter must be greater than ${exports.MIN_DELETE_AFTER}: ${deleteAfter}`);
52
73
  }
53
- if (deleteAfter < MIN_DELETE_AFTER) {
54
- throw new errors_1.MyTmpVpnError(`deleteAfter must be greater than ${MIN_DELETE_AFTER}: ${deleteAfter}`);
74
+ if (deleteAfter > exports.MAX_DELETE_AFTER) {
75
+ throw new errors_1.InvalidVpnConfigError(`deleteAfter must be lesser than ${exports.MAX_DELETE_AFTER}: ${deleteAfter}`);
55
76
  }
56
- if (deleteAfter > MAX_DELETE_AFTER) {
57
- throw new errors_1.MyTmpVpnError(`deleteAfter must be lesser than ${MAX_DELETE_AFTER}: ${deleteAfter}`);
77
+ return deleteAfter;
78
+ }
79
+ // Return whether the given vpn should be taken into account when computing
80
+ // whether a given user has reached its quota or not
81
+ function vpnAgainstQuotaPredicate(uservpn) {
82
+ // We return true only when the vpn is in a state that is below Running, and not Failed.
83
+ // The idea is that those ones are either already running or will soon, so they have to be
84
+ // part of the quota. Others (Failed, and Deprovisioning, Deleted) are gone, so they should not count.
85
+ return (uservpn.vpn.state !== VpnState.Failed) && (toRank(uservpn.vpn.state) <= toRank(VpnState.Running));
86
+ }
87
+ exports.vpnAgainstQuotaPredicate = vpnAgainstQuotaPredicate;
88
+ function validateVpnNbAgainstQuota(vpnNb, quota) {
89
+ if (vpnNb >= quota) {
90
+ throw new errors_1.InvalidUserConfigError(`User already has ${quota} active vpns. Quota reached.`);
58
91
  }
92
+ return vpnNb;
93
+ }
94
+ exports.validateVpnNbAgainstQuota = validateVpnNbAgainstQuota;
95
+ // vpnConfig is of type any here because it could come from user input such as json
96
+ function checkValidConfig(userId, currentBalance, vpnConfig) {
97
+ const maxPeanuts = checkValidMaxPeanuts(userId, currentBalance, vpnConfig.maxPeanuts);
98
+ const type = checkValidVpnType(vpnConfig.type);
99
+ const deleteAfter = checkValidDeleteAfter(vpnConfig.deleteAfter);
100
+ const result = {
101
+ maxPeanuts,
102
+ type,
103
+ deleteAfter
104
+ };
105
+ return result;
106
+ }
107
+ exports.checkValidConfig = checkValidConfig;
108
+ function checkValidMaxPeanuts(userId, currentBalance, maxPeanuts) {
109
+ const peanuts = Number.parseFloat(maxPeanuts);
110
+ if (Number.isNaN(peanuts)) {
111
+ throw new errors_1.InvalidVpnConfigError(`Config maxPeanuts is invalid: ${maxPeanuts}`);
112
+ }
113
+ console.log(`Validating peanuts for ${userId}, peanuts: ${peanuts}, currentBalance: ${currentBalance}`);
114
+ return validateMaxPeanuts(peanuts, currentBalance);
115
+ }
116
+ function checkValidVpnType(jsonType) {
117
+ if (!jsonType) {
118
+ throw new errors_1.InvalidVpnConfigError(`Config type is null or undefined: ${jsonType}`);
119
+ }
120
+ const type = jsonType;
121
+ if (!type || !Object.values(VpnType).includes(type)) {
122
+ throw new errors_1.InvalidVpnConfigError(`Config type is invalid: ${jsonType}`);
123
+ }
124
+ return type;
125
+ }
126
+ function checkValidDeleteAfter(jsonDeleteAfter) {
127
+ const deleteAfter = Number.parseInt(jsonDeleteAfter);
128
+ return validateDeleteAfter(deleteAfter);
59
129
  }
60
- exports.throwIfNotValidDeleteAfter = throwIfNotValidDeleteAfter;
61
130
  function newVpn(region, config, state) {
62
131
  // e.g: ap-northeast-3
63
132
  if (!/[a-z]{2}-[a-z]+-[1-9]+/.test(region)) {
@@ -66,8 +135,8 @@ function newVpn(region, config, state) {
66
135
  const createdAt = new Date();
67
136
  const vpnId = `${dateToId(createdAt)}@${region}`;
68
137
  // Assert we will be able to deserialize later on...
69
- throwIfNotValidVpnId(vpnId);
70
- throwIfNotValidDeleteAfter(config.deleteAfter);
138
+ validateVpnId(vpnId);
139
+ validateDeleteAfter(config.deleteAfter);
71
140
  return {
72
141
  vpnId,
73
142
  createdAt,
@@ -78,8 +147,8 @@ function newVpn(region, config, state) {
78
147
  }
79
148
  exports.newVpn = newVpn;
80
149
  function getVpnFrom(vpnId, state, config) {
81
- throwIfNotValidVpnId(vpnId);
82
- throwIfNotValidDeleteAfter(config.deleteAfter);
150
+ validateVpnId(vpnId);
151
+ validateDeleteAfter(config.deleteAfter);
83
152
  const tokens = vpnId.split('@');
84
153
  if (tokens.length !== 2) {
85
154
  throw new errors_1.MyTmpVpnError(`Incorrect vpnId: ${vpnId}`);
@@ -122,7 +191,7 @@ function idToDate(id) {
122
191
  // https://git.zx2c4.com/wireguard-android/tree/tunnel/src/main/java/com/wireguard/android/backend/Tunnel.java#n19
123
192
  // This function is to transform from vpnIn to Wireguard mobile application requirements
124
193
  function vpnIdToWgFileName(vpnId) {
125
- throwIfNotValidVpnId(vpnId);
194
+ validateVpnId(vpnId);
126
195
  // The vpnId is made of YYYYMMDDHHmmssmss@region
127
196
  // Let's keep it simple: vpn is mostly temporary, so keeping
128
197
  // the year is useless.
@@ -173,3 +242,35 @@ function vpnIdToWgFileName(vpnId) {
173
242
  return result;
174
243
  }
175
244
  exports.vpnIdToWgFileName = vpnIdToWgFileName;
245
+ function vpnToClient(vpn) {
246
+ return {
247
+ vpnId: vpn.vpnId,
248
+ state: vpn.state,
249
+ region: vpn.region,
250
+ createdAt: vpn.createdAt,
251
+ config: {
252
+ type: vpn.config.type,
253
+ deleteAfter: vpn.config.deleteAfter,
254
+ maxPeanuts: (0, peanuts_1.peanutsToClient)(vpn.config.maxPeanuts),
255
+ // terminationReason: userVpn.vpn.config.terminationReason,
256
+ // terminationMessage: userVpn.vpn.config.terminationMessage,
257
+ // terminationCode: userVpn.vpn.config.terminationCode,
258
+ // terminationType: userVpn.vpn.config.terminationType,
259
+ }
260
+ };
261
+ }
262
+ exports.vpnToClient = vpnToClient;
263
+ function metricsToClient(metrics) {
264
+ return (metrics != null)
265
+ ? {
266
+ duration: metrics.duration,
267
+ bytes: metrics.bytes,
268
+ peanuts: (0, peanuts_1.peanutsToClient)(metrics.peanuts)
269
+ }
270
+ : {
271
+ duration: Number.NaN,
272
+ bytes: Number.NaN,
273
+ peanuts: Number.NaN,
274
+ };
275
+ }
276
+ exports.metricsToClient = metricsToClient;
@@ -5,12 +5,17 @@ const errors_1 = require("../src/errors");
5
5
  const vpn_1 = require("../src/models/vpn");
6
6
  describe('Testing VpnState', () => {
7
7
  it('should check that all VpnState are ordered properly', async () => {
8
- expect((0, vpn_1.rank)(vpn_1.VpnState.Creating) <= (0, vpn_1.rank)(vpn_1.VpnState.Created)).toBe(true);
9
- expect((0, vpn_1.rank)(vpn_1.VpnState.Created) <= (0, vpn_1.rank)(vpn_1.VpnState.Provisioning)).toBe(true);
10
- expect((0, vpn_1.rank)(vpn_1.VpnState.Provisioning) <= (0, vpn_1.rank)(vpn_1.VpnState.Running)).toBe(true);
11
- expect((0, vpn_1.rank)(vpn_1.VpnState.Paused) <= (0, vpn_1.rank)(vpn_1.VpnState.Running)).toBe(true);
12
- expect((0, vpn_1.rank)(vpn_1.VpnState.Running) <= (0, vpn_1.rank)(vpn_1.VpnState.Deprovisioning)).toBe(true);
13
- expect((0, vpn_1.rank)(vpn_1.VpnState.Deprovisioning) <= (0, vpn_1.rank)(vpn_1.VpnState.Deleted)).toBe(true);
8
+ expect((0, vpn_1.toRank)(vpn_1.VpnState.Creating) <= (0, vpn_1.toRank)(vpn_1.VpnState.Created)).toBe(true);
9
+ expect((0, vpn_1.toRank)(vpn_1.VpnState.Created) <= (0, vpn_1.toRank)(vpn_1.VpnState.Provisioning)).toBe(true);
10
+ expect((0, vpn_1.toRank)(vpn_1.VpnState.Provisioning) <= (0, vpn_1.toRank)(vpn_1.VpnState.Running)).toBe(true);
11
+ expect((0, vpn_1.toRank)(vpn_1.VpnState.Paused) <= (0, vpn_1.toRank)(vpn_1.VpnState.Running)).toBe(true);
12
+ expect((0, vpn_1.toRank)(vpn_1.VpnState.Running) <= (0, vpn_1.toRank)(vpn_1.VpnState.Deprovisioning)).toBe(true);
13
+ expect((0, vpn_1.toRank)(vpn_1.VpnState.Deprovisioning) <= (0, vpn_1.toRank)(vpn_1.VpnState.Deleted)).toBe(true);
14
+ });
15
+ it('should check that state == fromRank(toRank(state))', async () => {
16
+ Object.values(vpn_1.VpnState).forEach(state => {
17
+ expect(state === (0, vpn_1.fromRank)((0, vpn_1.toRank)(state))).toBe(true);
18
+ });
14
19
  });
15
20
  });
16
21
  describe('Testing Vpn constructors', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mytmpvpn/mytmpvpn-common",
3
- "version": "4.0.1",
3
+ "version": "5.0.0",
4
4
  "description": "Common library for all MyTmpVpn related projects",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [