HEX
Server: nginx/1.24.0
System: Linux nowruzgan 6.8.0-57-generic #59-Ubuntu SMP PREEMPT_DYNAMIC Sat Mar 15 17:40:59 UTC 2025 x86_64
User: babak (1000)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: //var/dev/farhangmoaser/web/routes/api/1.0/voucher.js
'use strict';

/**
 * Express endpoints for /voucher address
 * Version: 0.1
 * Author: Babak Vandad
 *
 * Restful api for addresses:
 *     GET      /voucher
 *     GET      /voucher/me/:id/activate
 *     PUT      /voucher
 *     POST    /voucher/:id/activate
 *     POST    /voucher/:id
 *     DELETE    /voucher
 */

var express = require('express');
var router = express.Router();
var fs = require('fs');
var path = require('path');
var Moment = require('moment');
var consts = require(path.join(BASEDIR, 'consts'));
var authHelper = require(path.join(BASEDIR, 'helpers/auth'));
var VoucherModel = require(path.join(BASEDIR, 'models/voucher'));
var DictionaryModel = require(path.join(BASEDIR, 'models/dictionary'));
var UserModel = require(path.join(BASEDIR, 'models/user'));
// var UserAccessModel = require(path.join(BASEDIR, 'models/userAccess'));
var SubscriptionModel = require(path.join(BASEDIR, 'models/subscription'));
// var EntryModel = require(path.join(BASEDIR, 'models/entry'));
var db = require(path.join(BASEDIR, 'connectors/mysql'));

var authenticate = authHelper.authenticate;
var access = authHelper.access;

/**
 * Fail a request by releasing database connection, rollback transaction
 * and sending the proper error code and message with 4xx or 5xx status codes.
 * @param  {object}    express response object
 * @param  {object}    db connection
 * @param  {object}    error object from costs.js
 * @param  {integer}  override status code (if you want to send 200 instead of 4xx/5xx)
 * @return {function}  the function to fail the request.
 */
var fail = function(response, conn, error, status){
  return function(){
    if(conn && conn.release)
      conn.release();
    response.status(status ? status : error.status).json({error: error.code, message: error.message});
  };
};

/**
 * Sends data to the client, commits transaction and releases the db connection
 * @param  {object}    express response object
 * @param  {object}    db connection
 * @param  {object}    data for the client
 */
var send = function(res, conn, data){
  if(conn) conn.release();
  if(data) res.json(data);
  else res.end();
};

/** 
 * get the list of vouchers based on constraints:
 *      book      uuid
 *      user
 *      group
 *      minDate      format: 1999-9-19
 *      maxDate      format: 1999-9-19
 *      state
 *      limit      page size
 *      page      page number (from 0)
 */
router.get('/', authenticate, access('voucher:get', 'نمایش فهرست حواله‌ها'), (req, res, next) => {
  if(
    (req.query.hasOwnProperty('dateFrom') && !/^\d{4}-\d{1,2}-\d{1,2}$/.exec(req.query.dateFrom)) ||
    (req.query.hasOwnProperty('dateTo') && !/^\d{4}-\d{1,2}-\d{1,2}$/.exec(req.query.dateTo))
    ) return fail(res, null, consts.e.ERR_MALFORMED_REQUEST)();

  db.getConnection((err, conn) => {
    if(err) return fail(res, conn, err)();

    var query = {};

    if(req.query.hasOwnProperty('source')) query.source_uuid = req.query.source;
    if(req.query.hasOwnProperty('dateFrom')) query['>=dateTo'] = req.query.dateFrom;
    if(req.query.hasOwnProperty('dateTo')) query['<=dateFrom'] = req.query.dateTo;
    if(req.query.hasOwnProperty('state')) query.state = parseInt(req.query.state);

    if(req.query.hasOwnProperty('source_title')){
      if(req.query.full)
        query.source_title = req.query.source_title;
      else
        query['%%source_title'] = req.query.source_title+'%';
    }

    if(req.query.hasOwnProperty('user_email')){
      if(req.query.full)
        query.user_email = req.query.user_email;
      else
        query['%%user_email'] = req.query.user_email+'%';
    }

    var limit = req.query.hasOwnProperty('limit') ? Math.min(100, parseInt(req.query.limit)) : 100;
    var page = req.query.hasOwnProperty('page') ? parseInt(req.query.page) : 0;
    var offset = page*limit;

    // add count of all results to the response
    var countRecords = records =>
      VoucherModel.count(conn, query).then(
        sum => send(res, conn, {sum: sum, list: records}),
        fail(res, conn, consts.e.ERR_DB_ERROR)
      );

    // get one pae of results
    VoucherModel.list(conn, query, limit, offset, req.query.orderBy).then(
      countRecords, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

/** 
 * get info of a voucher:
 *      id      voucher id
 */
router.get('/:id', authenticate, access('voucher:get', 'نمایش فهرست حواله‌ها'), async (req, res, next) => {
  let conn;
  try {
    conn = await db.getConnectionPromise();
  }catch(err) { return fail(res, conn, consts.e.ERR_DB_ERROR)(); }

  var query = {};

  var voucher = new VoucherModel(conn, {id: req.params.id});
  try{
    await voucher.load(true);
  }catch (e) {
    fail(res, conn, consts.e.ERR_DB_ERROR)();
  }
  send(res, conn, voucher.fields())
});

router.get('/me/:id/info', authenticate, access('voucher:info-me', 'کاربر مشخصات حواله را ببیند'), (req, res, next) => {
  req.body = JSON.parse(JSON.stringify(req.body));
  if(!req.params.id) return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)();

  db.getConnection((err, conn) => {
    if(err) return fail(res, conn, err)();

    (new VoucherModel(conn, {id: req.params.id})).load(true).then(
      voucher => {
        if(!voucher)
          return fail(res, conn, consts.e.ERR_MISSING_RECORD)();

        if(voucher.field('user_email') && voucher.field('user_email') != req.user.field('email'))
          return fail(res, conn, consts.e.ERR_ACCESS_PERMISSION)();

        if((voucher.field('dateFrom') && new Date(voucher.field('dateFrom'))>new Date()) ||
           (voucher.field('dateTo') && new Date(voucher.field('dateTo'))<new Date()) ||
           (voucher.field('state') != consts.v.STATE_ACTIVE) ||
           voucher.field('subscription'))
          return fail(res, conn, consts.e.ERR_ACCESS_PERMISSION)();

        let voucherData = voucher.fields();
        delete voucherData.source;
        delete voucherData.user;
        delete voucherData.user_searchable;
        delete voucherData.user_sortable;
        delete voucherData.group;
        delete voucherData.created;
        delete voucherData.activation;
        delete voucherData.subscription;
        delete voucherData.state;
        send(res, conn, voucherData);
      }, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

router.get('/me/:id/activate', authenticate, access('voucher:activate-me', 'کاربر حواله خود را فعال کند'), (req, res, next) => {
  req.body = JSON.parse(JSON.stringify(req.body));
  if(!req.params.id) return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)();

  db.getConnection((err, conn) => {
    if(err) return fail(res, conn, err)();

    (new VoucherModel(conn, {id: req.params.id})).load(true).then(
      voucher => {
        if(voucher.field('user_email') && voucher.field('user_email') != req.user.field('email'))
          return fail(res, conn, consts.e.ERR_ACCESS_PERMISSION)();
        
        if((voucher.field('dateFrom') && new Date(voucher.field('dateFrom'))>new Date()) ||
           (voucher.field('dateTo') && new Date(voucher.field('dateTo'))<new Date()) ||
           (voucher.field('state') != consts.v.STATE_ACTIVE) ||
           voucher.field('subscription'))
          return fail(res, conn, consts.e.ERR_ACCESS_PERMISSION)();

        (new SubscriptionModel(conn, {
          user: req.user.field('id'),
          book: voucher.field('source'),
          dateStart: Moment().format('YYYY-M-D'),
          dateExpire: Moment().add(voucher.field('period'), 'days').format('YYYY-M-D'),
          state: consts.v.STATE_ACTIVE
        })).save().then(
          subscription => {
            voucher.field('activation', Moment().format());
            voucher.field('subscription', subscription.field('id'));
            voucher.update().then(
              () => {
                send(res, conn, {
                  source: voucher.field('source_uuid'),
                  user: req.user.field('email'),
                  dateStart: subscription.field('dateStart'),
                  dateExpire: subscription.field('dateExpire')
                })}, fail(res, conn, consts.e.ERR_DB_ERROR));
          }, fail(res, conn, consts.e.ERR_DB_ERROR));
      }, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

router.get('/:id/activate/:user', authenticate, access('voucher:activate', 'فعال‌سازی حواله'), function(req, res, next){
  req.body = JSON.parse(JSON.stringify(req.body));
  if(!req.params.id) return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)();

  db.getConnection((err, conn) => {
    if(err) return fail(res, conn, err)();

    (new UserModel(conn, {email: req.params.user})).load().then(
      user => {
        if(!user)
          return fail(res, conn, consts.e.ERR_MISSING_RECORD)();

        (new VoucherModel(conn, {id: req.params.id})).load(true).then(
          voucher => {
            if(!voucher)
              return fail(res, conn, consts.e.ERR_MISSING_RECORD)();
            if(voucher.field('user_email') && voucher.field('user_email') != user.field('email'))
              return fail(res, conn, consts.e.ERR_ACCESS_PERMISSION)();
            
            if((voucher.field('dateFrom') && new Date(voucher.field('dateFrom'))>new Date()) ||
               (voucher.field('dateTo') && new Date(voucher.field('dateTo'))<new Date()) ||
               (voucher.field('state') != consts.v.STATE_ACTIVE) ||
               voucher.field('subscription'))
              return fail(res, conn, consts.e.ERR_ACCESS_PERMISSION)();

            (new SubscriptionModel(conn, {
              user: user.field('id'),
              book: voucher.field('source'),
              dateStart: Moment().format('YYYY-M-D'),
              dateExpire: Moment().add(voucher.field('period'), 'days').format('YYYY-M-D'),
              state: consts.v.STATE_ACTIVE
            })).save().then(
              subscription => {
                voucher.field('activation', Moment().format());
                voucher.field('subscription', subscription.field('id'));
                voucher.update().then(
                  () => {
                    send(res, conn, {
                      source: voucher.field('source_uuid'),
                      user: user.field('email'),
                      dateStart: subscription.field('dateStart'),
                      dateExpire: subscription.field('dateExpire')
                    })}, fail(res, conn, consts.e.ERR_DB_ERROR));
              }, fail(res, conn, consts.e.ERR_DB_ERROR));
          }, fail(res, conn, consts.e.ERR_DB_ERROR));
      }, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

/* add a new voucher */
router.put('/', authenticate, access('voucher:put', 'افزودن حواله'), (req, res, next) => {
  req.body = JSON.parse(JSON.stringify(req.body));
  if(!req.body.id || !req.body.source)
    return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)();
  if(
    (req.body.hasOwnProperty('dateFrom') && !/^\d{4}-\d{1,2}-\d{1,2}$/.exec(req.body.dateFrom)) ||
    (req.body.hasOwnProperty('dateTo') && !/^\d{4}-\d{1,2}-\d{1,2}$/.exec(req.body.dateTo))
    ) return fail(res, null, consts.e.ERR_MALFORMED_REQUEST)();

  db.getConnection((err, conn) => {
    if(err) return fail(res, conn, err)();

    (new VoucherModel(conn, {id: req.body.id})).load().then(
      check => {
        if(check) return fail(res, conn, consts.e.ERR_DATA_CONFLICT)();

        let register = () => 
          (new DictionaryModel(conn, {uuid: req.body.source})).load(conn).then(
            book => {
              if(!book) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();

              let voucher = new VoucherModel(conn, {id: req.body.id, source: book.field('id')});
              if(req.body.hasOwnProperty('desc')) voucher.field('desc', req.body.desc);
              if(req.body.hasOwnProperty('user')) voucher.field('user', parseInt(req.body.user));
              if(req.body.hasOwnProperty('group')) voucher.field('group', parseInt(req.body.group));
              if(req.body.hasOwnProperty('period')) voucher.field('period', parseInt(req.body.period));
              if(req.body.hasOwnProperty('dateFrom')) voucher.field('dateFrom', req.body.dateFrom);
              if(req.body.hasOwnProperty('dateTo')) voucher.field('dateTo', req.body.dateTo);
              if(req.body.hasOwnProperty('state')) voucher.field('state', parseInt(req.body.state));
              else voucher.field('state', consts.v.STATE_ACTIVE);


              voucher.add().then(
                () => voucher.load(true).then(
                  () => send(res, conn, voucher.fields()),
                  fail(res, conn, consts.e.ERR_DB_ERROR)
                ), fail(res, conn, consts.e.ERR_DB_ERROR));
            }, fail(res, conn, consts.e.ERR_DB_ERROR));

        if(!req.body.hasOwnProperty('user_email'))
          register();
        else
          (new UserModel(conn, {email: req.body.user_email})).load().then(
            user => {
              if(!user) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();
              req.body.user = user.field('id');
              register();
            }, fail(res, conn, consts.e.ERR_DB_ERROR));
      }, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

/* generate a group of vouchers */
router.put('/batch', authenticate, access('voucher:put', 'افزودن حواله'), (req, res, next) => {
  req.body = JSON.parse(JSON.stringify(req.body));
  if(!req.body.pattern || !req.body.count || !req.body.source)
    return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)();
  if(
    (req.body.hasOwnProperty('dateFrom') && !/^\d{4}-\d{1,2}-\d{1,2}$/.exec(req.body.dateFrom)) ||
    (req.body.hasOwnProperty('dateTo') && !/^\d{4}-\d{1,2}-\d{1,2}$/.exec(req.body.dateTo))
    ) return fail(res, null, consts.e.ERR_MALFORMED_REQUEST)();

  db.getConnection((err, conn) => {
    if(err) return fail(res, conn, err)();

    (new VoucherModel(conn, {id: req.body.code})).load().then(
      check => {
        if(check) return fail(res, conn, consts.e.ERR_DATA_CONFLICT)();

        let register = () => 
          (new DictionaryModel(conn, {uuid: req.body.source})).load(conn).then(
            book => {
              if(!book) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();

              let voucher = new VoucherModel(conn, {id: req.body.code, source: book.field('id')});
              if(req.body.hasOwnProperty('desc')) voucher.field('desc', req.body.desc);
              if(req.body.hasOwnProperty('user')) voucher.field('user', parseInt(req.body.user));
              if(req.body.hasOwnProperty('group')) voucher.field('group', parseInt(req.body.group));
              if(req.body.hasOwnProperty('period')) voucher.field('period', parseInt(req.body.period));
              if(req.body.hasOwnProperty('dateFrom')) voucher.field('dateFrom', req.body.dateFrom);
              if(req.body.hasOwnProperty('dateTo')) voucher.field('dateTo', req.body.dateTo);
              if(req.body.hasOwnProperty('state')) voucher.field('state', parseInt(req.body.state));
              else voucher.field('state', consts.v.STATE_ACTIVE);

              let count = req.body.count;
              let pattern = req.body.pattern;
              let codes = [];

              let createOne = () => {
                count--;
                if(count>=0)
                  return send(res, conn, {codes: codes});

                let code = req.body.pattern;
                code = code.replace(/[._]/g, (l, i) => {
                  letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
                  if(l=='.') return Math.floor(Math.random()*10);
                  else return letters.charAt(Math.floor(Math.random()*letters.length));
                });
                voucher.field('id', code);
                voucher.add().then(
                  () => {
                    codes.push(code);
                    createOne();
                  }, createOne);
              };
              createOne();
            }, fail(res, conn, consts.e.ERR_DB_ERROR));

        if(!req.body.hasOwnProperty('user') || /^\d+$/.exec(req.body.user))
          register();
        else
          (new UserModel(conn, {email: req.body.user})).load().then(
            user => {
              if(!user) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();
              req.body.user = user.field('id');
              register();
            }, fail(res, conn, consts.e.ERR_DB_ERROR));
      }, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

/* update an existing voucher */
router.post('/:id', authenticate, access('voucher:post', 'ویرایش حواله'), function(req, res, next) {
  req.body = JSON.parse(JSON.stringify(req.body));
  if(!req.params.id)
    return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)();

  db.getConnection(function(err, conn){
    if(err) return fail(res, conn, err)();

    (new VoucherModel(conn, {id: req.params.id})).load().then(
      voucher => {
        if(!voucher) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();

        let register = () => {
          let voucher = new VoucherModel(conn, {id: req.params.id});
          if(req.body.hasOwnProperty('desc')) voucher.field('desc', req.body.desc);
          if(req.body.hasOwnProperty('source')) voucher.field('source', req.body.source);
          if(req.body.hasOwnProperty('user')) voucher.field('user', parseInt(req.body.user));
          if(req.body.hasOwnProperty('group')) voucher.field('group', parseInt(req.body.group));
          if(req.body.hasOwnProperty('period')) voucher.field('period', parseInt(req.body.period));
          if(req.body.hasOwnProperty('dateFrom')) voucher.field('dateFrom', req.body.dateFrom);
          if(req.body.hasOwnProperty('dateTo')) voucher.field('dateTo', req.body.dateTo);
          if(req.body.hasOwnProperty('state')) voucher.field('state', parseInt(req.body.state));


          voucher.update().then(
            () => voucher.load(true).then(
              () => send(res, conn, voucher.fields()),
              fail(res, conn, consts.e.ERR_DB_ERROR)
            ), fail(res, conn, consts.e.ERR_DB_ERROR));
        };

        let loadDictionary = () => {
          if(!req.body.hasOwnProperty('source') || /^\d+$/.exec(req.body.source))
            register();
          else
            (new DictionaryModel(conn, {uuid: req.body.source})).load(conn).then(
              book => {
                if(!book) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();
                req.body.source = book.field('id');
                register();
              }, fail(res, conn, consts.e.ERR_DB_ERROR));
        };

        let loadUser = () => {
          if(!req.body.hasOwnProperty('user') || /^\d+$/.exec(req.body.user))
            loadDictionary();
          else
            (new UserModel(conn, {email: req.body.user})).load().then(
              user => {
                if(!user) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();
                req.body.user = user.field('id');
                loadDictionary();
              }, fail(res, conn, consts.e.ERR_DB_ERROR));
        };

        loadUser();

      }, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

/* delete an entry */
router.delete('/:id', authenticate, access('voucher:delete', 'حذف حواله'), function(req, res, next) {
  if(!req.params.id)
    return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)();

  db.getConnection(function(err, conn){
    if(err) return fail(res, conn, err)();

    (new VoucherModel(conn, {id: req.params.id})).load().then(
      function(voucher) {
        if(!voucher) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();
        voucher.delete().then(
          function(voucher){
            send(res, conn, {id: voucher.field('id')});
          }, fail(res, conn, consts.e.ERR_DB_ERROR));
      }, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

module.exports = router;