@nlabs/reaktor 0.1.2 → 0.1.4

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.
Files changed (145) hide show
  1. package/.DS_Store +0 -0
  2. package/.eslintrc +18 -0
  3. package/lib/config.d.ts +21 -0
  4. package/lib/config.js +130 -0
  5. package/lib/data/conversations.d.ts +6 -0
  6. package/lib/data/conversations.js +197 -0
  7. package/lib/data/dynamodb.d.ts +8 -0
  8. package/lib/data/dynamodb.js +139 -0
  9. package/lib/data/email.d.ts +7 -0
  10. package/lib/data/email.js +163 -0
  11. package/lib/data/files.d.ts +16 -0
  12. package/lib/data/files.js +406 -0
  13. package/lib/data/groups.d.ts +13 -0
  14. package/lib/data/groups.js +354 -0
  15. package/lib/data/images.d.ts +12 -0
  16. package/lib/data/images.js +667 -0
  17. package/{src/data/index.ts → lib/data/index.d.ts} +1 -5
  18. package/lib/data/index.js +24 -0
  19. package/lib/data/ios.d.ts +6 -0
  20. package/lib/data/ios.js +302 -0
  21. package/lib/data/locations.d.ts +3 -0
  22. package/lib/data/locations.js +132 -0
  23. package/lib/data/messages.d.ts +9 -0
  24. package/lib/data/messages.js +248 -0
  25. package/lib/data/notifications.d.ts +5 -0
  26. package/lib/data/notifications.js +42 -0
  27. package/lib/data/payments.d.ts +11 -0
  28. package/lib/data/payments.js +748 -0
  29. package/lib/data/posts.d.ts +22 -0
  30. package/lib/data/posts.js +579 -0
  31. package/lib/data/reactions.d.ts +6 -0
  32. package/lib/data/reactions.js +218 -0
  33. package/lib/data/s3.d.ts +6 -0
  34. package/lib/data/s3.js +103 -0
  35. package/lib/data/search.d.ts +3 -0
  36. package/lib/data/search.js +98 -0
  37. package/lib/data/sms.d.ts +3 -0
  38. package/lib/data/sms.js +59 -0
  39. package/lib/data/subscription.d.ts +7 -0
  40. package/lib/data/subscription.js +284 -0
  41. package/lib/data/tags.d.ts +14 -0
  42. package/lib/data/tags.js +304 -0
  43. package/lib/data/users.d.ts +12 -0
  44. package/lib/data/users.js +310 -0
  45. package/lib/index.d.ts +3 -0
  46. package/lib/index.js +8 -0
  47. package/lib/types/apps.d.ts +43 -0
  48. package/lib/types/apps.js +2 -0
  49. package/lib/types/arangodb.d.ts +17 -0
  50. package/lib/types/arangodb.js +2 -0
  51. package/lib/types/auth.d.ts +10 -0
  52. package/lib/types/auth.js +2 -0
  53. package/lib/types/conversations.d.ts +6 -0
  54. package/lib/types/conversations.js +2 -0
  55. package/lib/types/email.d.ts +12 -0
  56. package/lib/types/email.js +2 -0
  57. package/lib/types/files.d.ts +26 -0
  58. package/lib/types/files.js +2 -0
  59. package/lib/types/google.d.ts +27 -0
  60. package/lib/types/google.js +2 -0
  61. package/lib/types/groups.d.ts +21 -0
  62. package/lib/types/groups.js +2 -0
  63. package/lib/types/images.d.ts +24 -0
  64. package/lib/types/images.js +2 -0
  65. package/{src/types/index.ts → lib/types/index.d.ts} +0 -4
  66. package/lib/types/index.js +22 -0
  67. package/lib/types/locations.d.ts +20 -0
  68. package/lib/types/locations.js +2 -0
  69. package/lib/types/messages.d.ts +12 -0
  70. package/lib/types/messages.js +2 -0
  71. package/lib/types/notifications.d.ts +19 -0
  72. package/lib/types/notifications.js +2 -0
  73. package/lib/types/payments.d.ts +114 -0
  74. package/lib/types/payments.js +2 -0
  75. package/lib/types/posts.d.ts +28 -0
  76. package/lib/types/posts.js +2 -0
  77. package/lib/types/reactions.d.ts +4 -0
  78. package/lib/types/reactions.js +2 -0
  79. package/lib/types/tags.d.ts +9 -0
  80. package/lib/types/tags.js +2 -0
  81. package/lib/types/users.d.ts +78 -0
  82. package/lib/types/users.js +2 -0
  83. package/lib/utils/analytics.d.ts +3 -0
  84. package/lib/utils/analytics.js +47 -0
  85. package/lib/utils/arangodb.d.ts +9 -0
  86. package/lib/utils/arangodb.js +98 -0
  87. package/lib/utils/auth.d.ts +7 -0
  88. package/lib/utils/auth.js +80 -0
  89. package/lib/utils/graphql.d.ts +1 -0
  90. package/lib/utils/graphql.js +7 -0
  91. package/{src/utils/index.ts → lib/utils/index.d.ts} +0 -4
  92. package/lib/utils/index.js +11 -0
  93. package/lib/utils/objects.d.ts +3 -0
  94. package/lib/utils/objects.js +34 -0
  95. package/lib/utils/redis.d.ts +1 -0
  96. package/lib/utils/redis.js +15 -0
  97. package/package.json +8 -7
  98. package/.vscode/extensions.json +0 -15
  99. package/.vscode/settings.json +0 -82
  100. package/lex.config.js +0 -4
  101. package/src/config.ts +0 -127
  102. package/src/data/conversations.ts +0 -181
  103. package/src/data/dynamodb.ts +0 -157
  104. package/src/data/email.ts +0 -163
  105. package/src/data/files.ts +0 -352
  106. package/src/data/groups.ts +0 -308
  107. package/src/data/images.ts +0 -606
  108. package/src/data/ios.ts +0 -249
  109. package/src/data/locations.ts +0 -114
  110. package/src/data/messages.ts +0 -237
  111. package/src/data/notifications.ts +0 -48
  112. package/src/data/payments.ts +0 -675
  113. package/src/data/posts.ts +0 -580
  114. package/src/data/reactions.ts +0 -186
  115. package/src/data/s3.ts +0 -117
  116. package/src/data/search.ts +0 -74
  117. package/src/data/sms.ts +0 -60
  118. package/src/data/subscription.ts +0 -228
  119. package/src/data/tags.ts +0 -230
  120. package/src/data/users.ts +0 -254
  121. package/src/index.ts +0 -7
  122. package/src/types/apps.ts +0 -56
  123. package/src/types/arangodb.ts +0 -23
  124. package/src/types/auth.ts +0 -20
  125. package/src/types/conversations.ts +0 -11
  126. package/src/types/email.ts +0 -17
  127. package/src/types/files.ts +0 -31
  128. package/src/types/google.ts +0 -37
  129. package/src/types/groups.ts +0 -27
  130. package/src/types/images.ts +0 -32
  131. package/src/types/locations.ts +0 -24
  132. package/src/types/messages.ts +0 -16
  133. package/src/types/notifications.ts +0 -26
  134. package/src/types/payments.ts +0 -129
  135. package/src/types/posts.ts +0 -34
  136. package/src/types/reactions.ts +0 -8
  137. package/src/types/tags.ts +0 -13
  138. package/src/types/users.ts +0 -89
  139. package/src/utils/analytics.ts +0 -41
  140. package/src/utils/arangodb.ts +0 -100
  141. package/src/utils/auth.ts +0 -61
  142. package/src/utils/graphql.ts +0 -7
  143. package/src/utils/objects.ts +0 -34
  144. package/src/utils/redis.ts +0 -17
  145. package/tsconfig.json +0 -45
@@ -1,186 +0,0 @@
1
- import {createHash, parseChar, parseId, parseNum} from '@nlabs/utils';
2
- import {aql, Database, EdgeCollection} from 'arangojs';
3
- import {AqlQuery} from 'arangojs/lib/cjs/aql-query';
4
- import {ArrayCursor} from 'arangojs/lib/cjs/cursor';
5
-
6
- import {ApiContext, QueryFilter, ReactionType, UserReactionQuery, UserReactionType, UserType} from '../types';
7
- import {logError, logException, useDb} from '../utils';
8
-
9
- const eventCategory: string = 'reactions';
10
-
11
- export const addGroupReaction = (context: ApiContext, params: UserReactionType = {}): Promise<boolean> => {
12
- const action: string = 'reaction';
13
- const {database, userId: sessionId} = context;
14
-
15
- if(!sessionId) {
16
- return logException({
17
- action,
18
- category: eventCategory,
19
- label: 'unauthorized',
20
- value: 'invalid_session'
21
- }, context).then(() => null);
22
- }
23
-
24
- const {id: itemId, value: itemValue = 'like'} = params;
25
- const formatItemId: string = parseId(itemId);
26
- const formatValue: string = parseChar(itemValue, 32);
27
- const edgeCollection = useDb(database).edgeCollection('hasReaction');
28
-
29
- // Remove existing likes
30
- const groupDocId: string = `groups/${formatItemId}`;
31
- const userDocId: string = `users/${sessionId}`;
32
- const edgeId: string = createHash(`reaction-${formatItemId}-${sessionId}`);
33
- const edge: any = {
34
- _key: edgeId,
35
- added: Date.now(),
36
- type: 'group',
37
- value: formatValue
38
- };
39
-
40
- return edgeCollection.save(edge, userDocId, groupDocId).then(() => true);
41
- };
42
-
43
- export const getGroupReactions = (context: ApiContext, params: UserReactionType = {}): Promise<UserReactionType> => {
44
- const action: string = 'getReactions';
45
- const {database} = context;
46
- const {id: groupId}: UserReactionType = params;
47
- const groupDocId: string = `groups/${parseId(groupId)}`;
48
-
49
- // Query
50
- const aqlQry: AqlQuery = aql`FOR g, r IN INBOUND ${groupDocId} hasReaction
51
- COLLECT reactionName = r.value INTO reactionItems
52
- RETURN {value: reactionName, count: LENGTH(reactionItems[*].r.value)}`;
53
-
54
- return useDb(database).query(aqlQry)
55
- .then((cursor: ArrayCursor) => cursor.all())
56
- .catch((error: Error) => logError({
57
- action,
58
- category: eventCategory,
59
- label: 'db_error'
60
- }, error, context).then(() => null));
61
- };
62
-
63
- export const getReactionsByUser = (context: ApiContext, groupId: string): Promise<UserReactionType[]> => {
64
- const action: string = 'getReactionsByUser';
65
- const {database, userId: sessionId} = context;
66
- const groupDocId: string = `groups/${parseId(groupId)}`;
67
- const userDocId: string = `users/${sessionId}`;
68
-
69
- // Query
70
- const aqlQry: AqlQuery = aql`FOR g, r IN INBOUND ${groupDocId} hasReaction
71
- FILTER r._from == ${userDocId}
72
- COLLECT reactionName = r.value INTO reactionItems
73
- RETURN {value: reactionName, count: LENGTH(reactionItems[*].r.value)}`;
74
-
75
- return useDb(database).query(aqlQry)
76
- .then((cursor: ArrayCursor) => cursor.all())
77
- .catch((error: Error) => logError({
78
- action,
79
- category: eventCategory,
80
- label: 'db_error'
81
- }, error, context).then(() => null));
82
- };
83
-
84
- export const getUsersByReaction = (context: ApiContext, params: UserReactionQuery = {}): Promise<UserType[]> => {
85
- const action: string = 'getUsersByReaction';
86
- const {database, userId: sessionId} = context;
87
-
88
- if(!sessionId) {
89
- return logException({
90
- action,
91
- category: eventCategory,
92
- label: 'unauthorized',
93
- value: 'invalid_session'
94
- }, context).then(() => null);
95
- }
96
-
97
- const {filters = [], id: groupId, value}: UserReactionQuery = params;
98
- const reaction = parseChar(value, 32);
99
- const groupDocId = `groups/${parseId(groupId)}`;
100
- const filterStr: string = filters
101
- .map((filter: QueryFilter) => {
102
- const {conditional, name, value: queryValue}: QueryFilter = filter;
103
- let filterCond: string = conditional;
104
-
105
- if(conditional !== '>=' && conditional !== '<=' && conditional !== '>' && conditional !== '<') {
106
- filterCond = '==';
107
- }
108
-
109
- switch(name) {
110
- case 'added':
111
- return `r.added ${filterCond} ${parseNum(queryValue)}`;
112
- default:
113
- return '';
114
- }
115
- })
116
- .concat([
117
- `r.value == "${reaction}"`,
118
- 'u._id == r._from'
119
- ])
120
- .join(' && ');
121
-
122
- // Query
123
- const aqlQry: string = `FOR g, r IN INBOUND "${groupDocId}" hasReaction
124
- FOR u IN users
125
- FILTER ${filterStr}
126
- RETURN u`;
127
-
128
- return useDb(database).query(aqlQry)
129
- .then((cursor: ArrayCursor) => cursor.all())
130
- .catch((error: Error) => logError({
131
- action,
132
- category: eventCategory,
133
- label: 'db_error'
134
- }, error, context).then(() => null));
135
- };
136
-
137
-
138
- export const postReaction = (context: ApiContext, postId: string, type: string = 'like'): Promise<ReactionType> => {
139
- const {database, userId: sessionId} = context;
140
- const formatItemId: string = parseId(postId);
141
- const db: Database = useDb(database);
142
- const edgeCollection: EdgeCollection = db.edgeCollection('reactions');
143
- const now: number = Date.now();
144
-
145
- // Remove existing reaction to post
146
- const postDocId: string = `posts/${formatItemId}`;
147
- const userDocId: string = `users/${sessionId}`;
148
- const aqlQry: AqlQuery = aql`FOR p, r IN INBOUND ${postDocId} reaction
149
- FILTER r.type == "posts" && r._from == ${userDocId}
150
- REMOVE r IN reactions
151
- RETURN r`;
152
-
153
- return db.query(aqlQry)
154
- .then(() => {
155
- const edgeId = createHash(`reaction-${postId}-${sessionId}`);
156
- const edge: any = {
157
- _key: edgeId,
158
- added: now,
159
- type: 'posts',
160
- value: type
161
- };
162
-
163
- return edgeCollection.save(edge, userDocId, postDocId)
164
- .then(() => {
165
- const reactionAqlQry: AqlQuery = aql`LET reactions = (
166
- FOR post, r IN INBOUND p._id reactions
167
- COLLECT reactionName = r.value INTO reactionItems
168
- RETURN {id: p._id, type: reactionName, count: LENGTH(reactionItems[*].r.value)}
169
- )
170
- RETURN reactions`;
171
-
172
- return db.query(reactionAqlQry)
173
- .then((cursor: ArrayCursor) => cursor.next())
174
- .then((result = {reactions: []}) => result)
175
- .catch((error: Error) => {
176
- throw error;
177
- });
178
- })
179
- .catch((error: Error) => {
180
- throw error;
181
- });
182
- })
183
- .catch((error: Error) => {
184
- throw error;
185
- });
186
- };
package/src/data/s3.ts DELETED
@@ -1,117 +0,0 @@
1
- /**
2
- * Copyright (c) 2019-Present, Nitrogen Labs, Inc.
3
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
4
- */
5
- import aws, {S3} from 'aws-sdk';
6
- import {
7
- DeleteObjectOutput,
8
- DeleteObjectRequest,
9
- DeleteObjectsOutput,
10
- DeleteObjectsRequest,
11
- GetObjectOutput,
12
- GetObjectRequest,
13
- HeadObjectOutput,
14
- HeadObjectRequest,
15
- PutObjectOutput,
16
- PutObjectRequest
17
- } from 'aws-sdk/clients/s3';
18
-
19
- import {Config} from '../config';
20
-
21
- // AWS S3
22
- // const eventCategory: string = 's3';
23
-
24
- export const s3Get = (params: GetObjectRequest): Promise<GetObjectOutput> => {
25
- return new Promise((resolve, reject) => {
26
- aws.config.update(Config.get('aws'));
27
- const s3: S3 = new S3();
28
-
29
- if(!params.Bucket) {
30
- params.Bucket = Config.get('aws.Bucket');
31
- }
32
-
33
- s3.getObject(params, (error: Error, output: GetObjectOutput) => {
34
- if(error) {
35
- return reject(error);
36
- }
37
-
38
- return resolve(output);
39
- });
40
- });
41
- };
42
-
43
- export const s3Head = (params: HeadObjectRequest): Promise<HeadObjectOutput> => {
44
- return new Promise((resolve, reject) => {
45
- aws.config.update(Config.get('aws'));
46
- const s3: S3 = new S3();
47
-
48
- if(!params.Bucket) {
49
- params.Bucket = Config.get('aws.Bucket');
50
- }
51
-
52
- s3.headObject(params, (error: Error, output: HeadObjectOutput) => {
53
- if(error) {
54
- return reject(error);
55
- }
56
- return resolve(output);
57
- });
58
- });
59
- };
60
-
61
- export const s3Put = (params: PutObjectRequest): Promise<PutObjectOutput> => {
62
- return new Promise((resolve, reject) => {
63
- aws.config.update(Config.get('aws'));
64
- const s3 = new aws.S3();
65
-
66
- if(!params.Bucket) {
67
- params.Bucket = Config.get('aws.Bucket');
68
- }
69
-
70
- if(!params.StorageClass) {
71
- params.StorageClass = 'REDUCED_REDUNDANCY';
72
- }
73
-
74
- s3.putObject(params, (error: Error, output: PutObjectOutput) => {
75
- if(error) {
76
- return reject(error);
77
- }
78
- return resolve(output);
79
- });
80
- });
81
- };
82
-
83
- export const s3Delete = (params: DeleteObjectRequest): Promise<DeleteObjectOutput> => {
84
- return new Promise((resolve, reject) => {
85
- aws.config.update(Config.get('aws'));
86
- const s3: S3 = new S3();
87
-
88
- if(!params.Bucket) {
89
- params.Bucket = Config.get('aws.Bucket');
90
- }
91
-
92
- s3.deleteObject(params, (error, output: DeleteObjectOutput) => {
93
- if(error) {
94
- return reject(error);
95
- }
96
- return resolve(output);
97
- });
98
- });
99
- };
100
-
101
- export const s3DeleteList = (params: DeleteObjectsRequest): Promise<DeleteObjectsOutput> => {
102
- return new Promise((resolve, reject) => {
103
- aws.config.update(Config.get('aws'));
104
- const s3: S3 = new S3();
105
-
106
- if(!params.Bucket) {
107
- params.Bucket = Config.get('aws.Bucket');
108
- }
109
-
110
- s3.deleteObjects(params, (error: Error, output: DeleteObjectsOutput) => {
111
- if(error) {
112
- return reject(error);
113
- }
114
- return resolve(output);
115
- });
116
- });
117
- };
@@ -1,74 +0,0 @@
1
- /**
2
- * Copyright (c) 2019-Present, Nitrogen Labs, Inc.
3
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
4
- */
5
- import {aql} from 'arangojs';
6
- import {AqlQuery} from 'arangojs/lib/cjs/aql-query';
7
- import {ArrayCursor} from 'arangojs/lib/cjs/cursor';
8
- import flatten from 'lodash/flatten';
9
- import uniqBy from 'lodash/uniqBy';
10
-
11
- import {ArangoDBLimit} from '../types/arangodb';
12
- import {ApiContext} from '../types/auth';
13
- import {UserType} from '../types/users';
14
- import {getLimit, logException, useDb} from '../utils';
15
- import {getDisplayName} from './users';
16
-
17
- const eventCategory: string = 'search';
18
-
19
- export const getSearchList = (context: ApiContext, query: string, from: number, to: number): Promise<UserType[]> => {
20
- const action: string = 'getList';
21
- const {database} = context;
22
- const isString: boolean = true;
23
-
24
- if(isString) {
25
- const fields: any[] = [
26
- {collection: 'users', field: 'first'},
27
- {collection: 'users', field: 'last'},
28
- {collection: 'users', field: 'email'},
29
- {collection: 'users', field: 'username'},
30
- {collection: 'users', field: 'name'}
31
- ];
32
-
33
- return Promise.all(fields.map((obj) => {
34
- const qry: string = `prefix:${query}`;
35
- const userQry: AqlQuery = aql`FOR u IN FULLTEXT(${obj.collection}, ${obj.field}, ${qry}, 50) RETURN u`;
36
- return useDb(database).query(userQry).then((cursor: ArrayCursor) => cursor.all());
37
- }))
38
- .then((results) => {
39
- const list = uniqBy(flatten(results), '_key');
40
-
41
- return list.map((item: UserType) => {
42
- const {_id: itemId, name: itemName} = item;
43
- const collectionIdList: string[] = itemId.split('/');
44
- const collection: string = collectionIdList[0];
45
- const name: string = collection === 'users' ? getDisplayName(item) : itemName;
46
-
47
- return {...item, name};
48
- });
49
- });
50
- }
51
-
52
- const limit: ArangoDBLimit = getLimit(from, to);
53
- const aqlQry: string = `FOR u IN users
54
- FILTER u.phone == "${query}"
55
- ${limit.aql}
56
- RETURN u`;
57
-
58
- return useDb(database).query(aqlQry)
59
- .then((cursor: ArrayCursor) => cursor.all())
60
- .then((list: UserType[] = []) => list.map((item = {}) => {
61
- const {_id: itemId, name: itemName} = item;
62
- const collectionIdList: string[] = itemId.split('/');
63
- const collection: string = collectionIdList[0];
64
- const name: string = collection === 'users' ? getDisplayName(item) : itemName;
65
-
66
- return {...item, name};
67
- }))
68
- .catch((error: Error) => logException({
69
- action,
70
- category: eventCategory,
71
- label: 'db_error',
72
- value: error.message
73
- }, context).then(() => null));
74
- };
package/src/data/sms.ts DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * Copyright (c) 2019-Present, Nitrogen Labs, Inc.
3
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
4
- */
5
- import {parsePhone} from '@nlabs/utils';
6
- import fs from 'fs';
7
- import twilioSdk from 'twilio';
8
-
9
- import {Config} from '../config';
10
- import {SMSParamsType, TwilioOptionsType} from '../types/notifications';
11
- import {appTemplate} from './email';
12
-
13
- // const eventCategory: string = 'sms';
14
-
15
- // Twilio
16
- export const sendSms = (params: SMSParamsType): any => {
17
- const {
18
- app = {},
19
- content,
20
- user = {}
21
- } = params;
22
-
23
- const country = user.country || 'US';
24
- const phone = parsePhone(user.phone, country);
25
- const templateContent: string = appTemplate(app, content);
26
-
27
- const twilio: any = twilioSdk(Config.get('twilio.sid'), Config.get('twilio.token'));
28
- const cfg: TwilioOptionsType = {
29
- body: templateContent,
30
- from: Config.get('twilio.number'),
31
- to: phone
32
- };
33
-
34
- return new Promise((resolve, reject) => {
35
- twilio.sendMessage(cfg, (error, data) => {
36
- if(error) {
37
- reject(new Error(error.error_message));
38
- } else {
39
- resolve(data);
40
- }
41
- });
42
- });
43
- }
44
-
45
- export const sendSmsTemplate = (templateName: string, smsParams: SMSParamsType): Promise<boolean> => {
46
- const {app, user, vars} = smsParams;
47
- try {
48
- const data = fs.readFileSync(`./templates/sms/${templateName}.txt`, 'utf8');
49
- const text = appTemplate(app, data, vars);
50
- const params = {app, text, user};
51
-
52
- return sendSms(params)
53
- .then((res) => res.status === 'queued')
54
- .catch((error: Error) => {
55
- throw error;
56
- });
57
- } catch(error) {
58
- return Promise.reject(error);
59
- }
60
- };
@@ -1,228 +0,0 @@
1
- /**
2
- * Copyright (c) 2019-Present, Nitrogen Labs, Inc.
3
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
4
- */
5
- import {createHash, parseChar, parseId, parseNum, parseVarChar} from '@nlabs/utils';
6
- import {aql} from 'arangojs';
7
- import {AqlQuery} from 'arangojs/lib/cjs/aql-query';
8
- import {ArrayCursor} from 'arangojs/lib/cjs/cursor';
9
- import {DateTime} from 'luxon';
10
- import * as stripe from 'stripe';
11
-
12
- import {Config} from '../config';
13
- import {ArangoDBLimit} from '../types/arangodb';
14
- import {ApiContext} from '../types/auth';
15
- import {PaymentInterval, PaymentPlan, PaymentSubscription} from '../types/payments';
16
- import {getLimit, logError, logException, useDb} from '../utils';
17
- import {getUser} from './users';
18
-
19
- const eventCategory: string = 'subscription';
20
-
21
- export const getPlanList = (context: ApiContext, from: number = 0, to: number = 30): Promise<PaymentPlan[]> => {
22
- const action: string = 'getList';
23
- const {database} = context;
24
- const limit: ArangoDBLimit = getLimit(from, to);
25
- const aqlQry: string = `FOR p IN plans
26
- ${limit.aql}
27
- SORT p.amount
28
- RETURN p`;
29
-
30
- return useDb(database).query(aqlQry)
31
- .then((cursor: ArrayCursor) => cursor.all())
32
- .then((list = []) => list)
33
- .catch((error: Error) => logError({
34
- action,
35
- category: eventCategory,
36
- label: 'db_error'
37
- }, error, {}).then(() => null).catch((error) => Promise.reject(error)));
38
- };
39
-
40
- export const getSubscription = (context: ApiContext): Promise<PaymentSubscription> => {
41
- const action: string = 'getItem';
42
- const {database, userId: sessionId} = context;
43
- const aqlQry: string = `FOR s IN subscriptions
44
- FILTER s.userId == ${sessionId} && s.cancelled > 0
45
- LET plan = FIRST(
46
- FOR p IN plans
47
- FILTER p._key == s.planId
48
- )
49
- LIMIT 1
50
- RETURN MERGE(s, {plan: plan})`;
51
-
52
- return useDb(database).query(aqlQry)
53
- .then((cursor: ArrayCursor) => cursor.next())
54
- .then((subscription = {}) => subscription)
55
- .catch((error: Error) => logError({
56
- action,
57
- category: eventCategory,
58
- label: 'db_error'
59
- }, error, {}).then(() => null).catch((error) => Promise.reject(error)));
60
- }
61
-
62
- export const addPlan = (context: ApiContext, item: PaymentPlan): Promise<PaymentPlan> => {
63
- const action: string = 'addPlan';
64
- const {database, userId: sessionId, userType} = context;
65
- const isAdmin: boolean = userType > 2;
66
-
67
- if(!isAdmin) {
68
- return logException({
69
- action,
70
- category: eventCategory,
71
- label: 'unauthorized',
72
- value: 'invalid_session'
73
- }, context).then(() => null);
74
- }
75
-
76
- const now: number = Date.now();
77
- const formatId: string = parseId(item.id);
78
- const planId: string = formatId === '' ? createHash(`tag-${sessionId}`) : formatId;
79
- const {amount, currency = 'USD', description, interval, intervalCount, name} = item;
80
- const formatAmount: number = parseNum(amount);
81
- const formatInterval: PaymentInterval = parseChar(interval, 5).toLowerCase() as PaymentInterval;
82
- const formatIntervalCnt: number = parseNum(intervalCount, 5);
83
- const formatName: string = parseVarChar(name, 32);
84
- const formatDesc: string = parseVarChar(description, 32);
85
-
86
- const insert: PaymentPlan = {
87
- _key: planId,
88
- added: now,
89
- amount: formatAmount,
90
- currency,
91
- description: formatDesc,
92
- interval: formatInterval,
93
- intervalCount: formatIntervalCnt,
94
- modified: now,
95
- name: formatName
96
- };
97
- const aqlQry: AqlQuery = aql`INSERT ${insert} IN plans RETURN NEW`;
98
-
99
- return useDb(database).query(aqlQry)
100
- .then((cursor: ArrayCursor) => cursor.next())
101
- .then((plan: PaymentPlan = {}) => {
102
- // Stripe
103
- const stripeClient = stripe(Config.get('stripe.token'));
104
- return stripeClient.plans
105
- .create({
106
- amount: formatAmount * 100,
107
- currency,
108
- id: planId,
109
- interval: formatInterval,
110
- interval_count: formatIntervalCnt,
111
- metadata: {
112
- },
113
- name: formatName,
114
- statement_descriptor: formatDesc
115
- })
116
- .then(() => plan);
117
- })
118
- .catch((error: Error) => logError({
119
- action,
120
- category: eventCategory,
121
- label: 'db_error'
122
- }, error, {}).then(() => null).catch((error) => Promise.reject(error)));
123
- };
124
-
125
- export const addSubscription = (context: ApiContext, item): Promise<PaymentSubscription> => {
126
- const action: string = 'addSubscription';
127
- const {database, userId: sessionId, userType} = context;
128
- const isAdmin: boolean = userType > 2;
129
-
130
- if(!isAdmin) {
131
- return logException({
132
- action,
133
- category: eventCategory,
134
- label: 'unauthorized',
135
- value: 'invalid_session'
136
- }, context).then(() => null);
137
- }
138
-
139
- const now: number = Date.now();
140
- const formatId: string = parseId(item.id);
141
- const subscriptionId: string = formatId === '' ? createHash(`tag-${sessionId}`) : formatId;
142
- const {planId, tax, trialEnd, trialPeriod} = item;
143
- const formatPlanId: string = parseId(planId);
144
- const formatTaxPercent: number = parseNum(tax) || 0;
145
- const formatTrialPeriod: number = parseNum(tax) || 0;
146
- let formatTrialEnd: number = trialEnd || Date.now();
147
-
148
- if(formatTrialPeriod) {
149
- formatTrialEnd = DateTime.local().plus({days: trialPeriod}).toMillis();
150
- }
151
-
152
- return getUser(context, sessionId)
153
- .then((user) => {
154
- const insert: PaymentSubscription = {
155
- _key: subscriptionId,
156
- added: now,
157
- cancelDate: 0,
158
- modified: now,
159
- planId: formatPlanId,
160
- tax: formatTaxPercent,
161
- trialEnd: formatTrialEnd,
162
- userId: sessionId
163
- };
164
- const aqlQry: AqlQuery = aql`INSERT ${insert} IN plans RETURN NEW`;
165
-
166
- return useDb(database).query(aqlQry)
167
- .then((cursor: ArrayCursor) => cursor.next())
168
- .then((subscription: PaymentSubscription = {}) => {
169
- // Stripe
170
- const stripeClient = stripe(Config.get('stripe.token'));
171
- return stripeClient.subscriptions
172
- .create({
173
- customer: user.stripeCustomerId,
174
- items: [
175
- {plan: formatPlanId}
176
- ],
177
- metadata: {
178
- userId: sessionId
179
- },
180
- tax_percent: formatTaxPercent * 100,
181
- trial_end: formatTrialEnd
182
- })
183
- .then(() => subscription);
184
- })
185
- .catch((error: Error) => logError({
186
- action,
187
- category: eventCategory,
188
- label: 'db_error'
189
- }, error, {}).then(() => null).catch((error) => Promise.reject(error)));
190
- });
191
- };
192
-
193
- export const deleteSubscription = (context: ApiContext, endDate: number): Promise<boolean> => {
194
- const action: string = 'deleteSubscription';
195
- const {database} = context;
196
- const now: number = Date.now();
197
- const formatEndDate: number = parseNum(endDate) || now;
198
-
199
- return getSubscription(context)
200
- .then((subscription) => {
201
- // Stripe
202
- const stripeClient = stripe(Config.get('stripe.token'));
203
- return stripeClient.subscriptions
204
- .del(subscription.transactionId, {
205
- subscription: formatEndDate
206
- })
207
- .then((stripeResponse) => {
208
- // Make sure we cancelled on Stripe before updating the db
209
- if(stripeResponse.cancelled || stripeResponse.cancel_at_period_end) {
210
- const update: PaymentSubscription = {
211
- cancelDate: formatEndDate,
212
- modified: now,
213
- planId: ''
214
- };
215
- const aqlQry: AqlQuery = aql`UPDATE s WITH ${update} IN subscriptions`;
216
-
217
- return useDb(database).query(aqlQry)
218
- .then(() => true)
219
- .catch((error: Error) => logError({
220
- action,
221
- category: eventCategory,
222
- label: 'db_error'
223
- }, error, {}).then(() => null).catch((error) => Promise.reject(error)));
224
- }
225
- return false;
226
- });
227
- });
228
- };