· 5 years ago · Jul 29, 2020, 03:00 PM
1// var url = process.env.MONGODB_VOUCHER_HOST || "mongodb://localhost:27017/mydb";
2// var url = "mongodb://"+mongodb_user_name+":"+mongodb_user_pw+"@cluster0-shard-00-00-21v6f.mongodb.net:27017,cluster0-shard-00-01-21v6f.mongodb.net:27017,cluster0-shard-00-02-21v6f.mongodb.net:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin";
3// var mongodb_user_name = process.env.MONGODB_VOUCHER_USER || "coinmode_voucher_db_user";
4// var mongodb_user_pw = process.env.MONGODB_VOUCHER_PASSWORD || "xepz2m7m7336!!";
5//var url = "mongodb+srv://"+mongodb_user_name+":"+mongodb_user_pw+"@cluster0-21v6f.mongodb.net/test?retryWrites=true";
6//var url = "mongodb://"+mongodb_user_name+":"+mongodb_user_pw+"@cluster0-shard-00-00-21v6f.mongodb.net:27017,cluster0-shard-00-01-21v6f.mongodb.net:27017,cluster0-shard-00-02-21v6f.mongodb.net:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin&retryWrites=true";
7var connection_status = "not connected";
8var g_dbo = null;
9const coinmode = require("./helpers/coinmode");
10const time_helpers = require('./helpers/time_helpers');
11const jsonfile = require('jsonfile')
12const path = require('path')
13global.mongo = require("mongodb");
14global.g_mongoclient = require("mongodb").MongoClient;
15const log = require( "./helpers/log" )
16const backup_to_keybase = require( "./helpers/backup_to_keybase" );
17
18/*
19 THE FOLLOWING VARIABLES ARE SENSITIVE TO THE MONGODB ACCOUNT
20 I CREATED AS PART OF GETTING STARTED.
21 IN FUTURE WE PROBABLY HAVE TO CHANGE THIS.
22*/
23let database_name = 'coinmode_vouchers_mongodb'
24var g_collection_name = process.env.VOUCHERS_DB_COLLECTION
25let url = process.env.VOUCHERS_MONGODB_URL
26
27exports.array_currencies =
28[
29 "bitcoin_test",
30 "bitcoin_main"
31]
32
33exports.connect = async function()
34{
35 try
36 {
37 if( connection_status === "not connected" )
38 {
39 var db = await g_mongoclient.connect(url, { useNewUrlParser: true } );
40 g_dbo = await db.db(database_name);
41 try
42 {
43 var collection = await g_dbo.createCollection( g_collection_name );
44 }
45 catch(e)
46 {
47 log( true, {err: e}, "Connection error when creating test:", JSON.stringify(e), '\n');
48 }
49 connection_status = "connected";
50 }
51 return g_dbo;
52 }
53 catch(e)
54 {
55 log( true, { err: e}, 'It is connection issue.');
56 return null;
57 }
58}
59
60
61
62exports.set = async function( id, data )
63{
64 var dbo = await this.connect();
65
66 // Create the new object to save
67 var myobj = {...data};
68 myobj['id'] = id;
69
70 var query =
71 {
72 "id" : id
73 }
74
75 // Upsert the document
76 try
77 {
78 await dbo.collection(g_collection_name).update(query, myobj, {upsert: true, multi: false});
79 }
80 catch(e)
81 {
82 log( true, { err: e }, "Error upserting document:"+id+" err:", JSON.stringify(e), '\n');
83 throw "Error saving document";
84 }
85 //await dbo.collection(g_collection_name).insertOne(myobj);
86
87}
88
89
90exports.get = async function( id, data )
91{
92 try
93 {
94 var dbo = await this.connect();
95
96 var myobj = {};
97 myobj['id'] = parseInt(id,10);
98
99 var doc = await dbo.collection(g_collection_name).findOne(myobj);
100 log( false, false, "Found document:", JSON.stringify(doc, null, 4), '\n' );
101 return doc;
102 }
103 catch(err_db)
104 {
105 log( true, { err: err_db }, err_db );
106 return null;
107 }
108}
109
110
111exports.list = async function( account_id )
112{
113 var dbo = await this.connect();
114 var data = {};
115 if( account_id != null )
116 {
117 data = {"account_id":account_id};
118 }
119
120 var docs = await dbo.collection(g_collection_name).find(data);
121 var results = await docs.toArray();
122 for ( let i = 0; i < results.length; i++ ) {
123
124 delete results[ i ]._id
125 delete results[ i ].voucher_password_mnemonic
126 delete results[ i ].voucher_id_mnemonic
127 delete results[ i ].voucher_password_int
128
129 }
130 log( false, false, "Found document:\n", JSON.stringify(results, null, 4), '\n');
131 return results;
132
133}
134
135exports.stats = async function()
136{
137
138 try {
139
140 let mongodb = await exports.connect()
141
142 try {
143
144 let stats = await mongodb.collection( g_collection_name ).stats()
145 let useful_stats = {
146
147 // This is in MBs
148 size: stats.size / ( 1024 * 1024 )
149 , count: stats.count
150 , avg_obj_size: stats.avgObjSize / ( 1024 * 1024 )
151 , storage_size: stats.storageSize / ( 1024 * 1024 )
152 , capped: stats.capped
153 , total_index_size: stats.totalIndexSize / ( 1024 * 1024 )
154 , operation_time: stats.operationTime
155
156 }
157 return useful_stats
158
159 } catch (e) {
160
161 log( true, {err: e},
162 "An error happened while trying to request for stats function."
163 , "Here is the error:"
164 , e
165 )
166
167 }
168
169 } catch ( e ) {
170
171 log( true, {err: e},
172 "An error happened while trying to connect the mongodb database."
173 , "Here iss the error:"
174 , e
175 )
176
177 }
178
179}
180
181exports.get_total_amount = async function() {
182
183 try {
184
185 let mongodb = await exports.connect()
186
187 try {
188
189 let total_amount = await mongodb.collection(g_collection_name).aggregate([ {
190 $group: {
191 _id: "$voucher_currency",
192 total: {
193 $sum: "$amount"
194 }
195 , average: {
196 $avg: "$amount"
197 }
198 , min: {
199 $min: "$amount"
200 }
201 , max: {
202 $max: "$amount"
203 }
204 }
205 } ] ).toArray()
206
207 return array_to_json( total_amount )
208
209 } catch (e) {
210
211 log( true, { err: e },
212 "An error happened while trying to run an aggregate query for total_amount()"
213 , "Here is the error:"
214 , e
215 )
216
217 }
218
219 } catch ( e ) {
220
221 log( true, { err: e },
222 "An error happened while trying to connect the mongodb database."
223 , "Here is the error:"
224 , e
225 )
226
227 }
228
229}
230
231exports.get_account_balance = async function() {
232
233 try
234 {
235 let post_json = {
236 play_token: app.get('play_token') || process.env.VOUCHER_PLAY_TOKEN
237 }
238
239 let get_balance_api = await coinmode.post(
240 "/v1/players/wallet/get_balances",
241 post_json
242 );
243
244 if ( get_balance_api.data.wallet_id ) {
245
246 let bitcoin_main = get_balance_api.data.balances.bitcoin_main.confirmed
247 let bitcoin_test = get_balance_api.data.balances.bitcoin_test.confirmed
248
249 return {
250 bitcoin_main,
251 bitcoin_test
252 }
253
254 } else {
255
256 log( true, { err: JSON.stringify(get_balance_api.data, null, 4) }, 'Get balances API error response', JSON.stringify(get_balance_api.data, null, 4) )
257
258 }
259
260 }
261 catch(e) {
262
263 log( true, { err: e }, 'An error happened in running /v1/players/wallet/get_balance', e )
264
265 }
266
267}
268
269exports.get_vouchers_redeemed = async function() {
270
271 try {
272
273 return await mongo_aggregate({
274 match: {
275 "epoch_redeemed": {
276 $gt: 0
277 }
278 }
279 })
280
281 } catch(e) {
282
283 log( true, { err: e },
284 "MongoDB error for running a query in"
285 , exports.get_vouchers_redeemed.name, e
286 )
287
288 }
289
290}
291
292exports.get_vouchers_active = async function() {
293
294 try {
295
296 return await mongo_aggregate({
297 match: {
298 "epoch_valid_until": {
299 $gt: time_helpers.now_as_epoch()
300 }
301 , "epoch_cancelled": {
302 $lte: 0
303 }
304 , "epoch_redeemed": {
305 $lte: 0
306 }
307 }
308 })
309
310 } catch(e) {
311
312 log( true, { err: e },
313 "MongoDB error for running a query in"
314 , exports.get_vouchers_active.name, e
315 )
316
317 }
318
319}
320
321exports.get_vouchers_expired = async function() {
322
323 try {
324
325 return await mongo_aggregate({
326 match: {
327 "epoch_valid_until": {
328 $lt: time_helpers.now_as_epoch()
329 }
330 }
331 })
332
333 } catch(e) {
334
335 log( true, { err: e },
336 "MongoDB error for running a query in"
337 , exports.get_vouchers_expired.name, e
338 )
339
340 }
341
342}
343
344/*
345 MONGODB RETURNS A PROMISE OBJECT
346 TO GET SENSIBLE DATA OUT OF IT
347 toArray() HAS TO BE USED. THIS
348 FUNCTION SIMPLY RETURNS THE ARRAY
349 AS AN OBJECT SO THAT INNER CONTENT
350 MAY BE ACCESSED BY A KEY THAN INDEX.
351*/
352function array_to_json( array, id = "_id" ) {
353
354 let json = {}
355
356 for ( let i = 0; i < array.length; i++ ) {
357
358 json[ array[ i ][ id ] ] = array[ i ]
359
360 }
361
362 return json
363
364}
365
366/*
367 A SMALL EFFORT TO MINIMIZE CODE DUPLICATION
368 AND TO HAVE ONE MAIN LINE OF CONTROL.
369 STATS RELATING TO METRICS ARE ALL OF THE SAME
370 TYPE JUST DIFFERENT CONDITIONS SO IT MADE
371 SENSE TO HAVE ONE FUNCTION DEAL WITH IT ALL.
372 THIS RETURNS A SIMPLE JSON OBJECT.
373*/
374async function mongo_aggregate({
375 match
376 , group_by_id = "$voucher_currency"
377 , group_field = "$amount"
378}){
379
380 try {
381
382 let mongodb = await exports.connect()
383 let vouchers = await mongodb.collection(g_collection_name)
384
385 let data = await vouchers.aggregate([
386 {
387 $match: match
388 }
389 , {
390 $group: {
391 _id: group_by_id
392 , total: {
393 $sum: group_field
394 }
395 , count: {
396 $sum: 1
397 }
398 , average: {
399 $avg: group_field
400 }
401 , min: {
402 $min: group_field
403 }
404 , max: {
405 $max: group_field
406 }
407 }
408 }
409 ]).toArray()
410
411 return array_to_json( data )
412
413 } catch(e) {
414
415 log( true, { err: e },
416 "MongoDB error for running a query in"
417 , mongo_aggregate.name, e
418 )
419
420 }
421
422}
423
424/**
425 * Bespoke backup functionality for
426 * MongoDB. At the moment it literally
427 * only saves files to the local folder
428 *
429 * If tweaked a little bit by saving the
430 * file in the cloud storage - it can be
431 * made fully continuous back up
432 */
433exports.backup = async function()
434{
435 let dbo = await this.connect();
436 let docs = await dbo.collection(g_collection_name).find();
437 let results = await docs.toArray();
438 let file_name = new Date().toISOString() + '.json'
439 let file_path = path.join( __dirname + '/../backup' )
440 let full_path = path.join( file_path, file_name )
441
442 try {
443
444 await jsonfile.writeFile( full_path, results, { spaces: 4, EOL: '\r\n' } )
445 console.info(
446 'Backup successfully backed up at'
447 , full_path
448 )
449
450 } catch (e) {
451
452 log( true, { err: e },
453 'Error occurred while writing the backup file'
454 , e
455 )
456
457 }
458
459}
460
461exports.keybase_backup = async function()
462{
463 let dbo = await this.connect()
464 let docs = await dbo.collection(g_collection_name).find()
465 let results = await docs.toArray()
466 await backup_to_keybase( results )
467}
468
469/**
470 * A restore function that takes filename from
471 * the backup folder
472 */
473exports.restore = async function( file_name ) {
474
475 let file_path = path.join( __dirname + '/../backup' )
476 let full_path = path.join( file_path, file_name )
477 let mongo
478 let backup
479
480 try {
481
482 backup = await jsonfile.readFile( full_path )
483 console.info(
484 '\n\n\nThis is what is going to be restored . . .\n\n\n'
485 , JSON.stringify(
486 backup,
487 null,
488 4
489 )
490 )
491
492 } catch (e) {
493
494 log( true, { err: e },
495 'Error occurred while reading the backup file'
496 , e
497 )
498
499 }
500
501 if ( backup.length ) {
502
503 let green_light
504 try {
505
506 log( false, false,
507 '\n\n\nEmptying out the collection . . .\n\n\n'
508 )
509 mongo = await exports.connect()
510 let removed = await mongo.collection( g_collection_name ).remove({})
511 log( false, false,
512 'Removed result',
513 JSON.stringify( removed.result, null, 4 )
514 )
515 green_light = true
516
517 } catch (e) {
518
519 log( true, { err: e },
520 'Error occurred while trying to do'
521 , 'full clean up of current collection . . .'
522 , e
523 )
524
525 }
526
527
528 if ( green_light ) {
529
530 log( false, false, '\n\nDoing the restore . . .\n\n')
531 let result = await mongo
532 .collection(g_collection_name)
533 .insert(
534 backup,
535 {
536 ordered: false
537 }
538 )
539 log( false, false,
540 '\n\n\nInserted count:', result.insertedCount
541 , '\nResult:'
542 , JSON.stringify(
543 result,
544 null,
545 4
546 )
547 )
548
549 }
550
551 } else
552 {
553
554 log( false, false,
555 'Either there was no backup data found or mongo instance is null'
556 , 'Backed up data length:'
557 , backup.length
558 , 'Mongo instance is null:'
559 , g_dbo === null
560 )
561
562 }
563
564}