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/subscription.js
/**
 * Express endpoints for /subscription address
 * Version: 0.1
 * Author: Babak Vandad
 *
 * Restful api for these addresses:
 *     GET      /subscription/me
 *     GET      /subscription/:id
 *     GET      /subscription/
 *     PUT      /subscription/
 *     POST    /subscription/:id
 *     DELETE    /subscription/:id
 */

var express = require('express');
var router = express.Router();
var path = require('path');
var consts = require(path.join(BASEDIR, 'consts'));
var authHelper = require(path.join(BASEDIR, 'helpers/auth'));
var DictionaryModel = require(path.join(BASEDIR, 'models/dictionary'));
var SubscriptionModel = require(path.join(BASEDIR, 'models/subscription'));
var UserAccessModel = require(path.join(BASEDIR, 'models/userAccess'));
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){
  conn.release();
  if(data)
    res.json(data);
  else
    res.end();
};

/**
 * makes a searchable string (does not remove spaces)
 * @param  {string} str user input or given entry title
 * @return {string}     searchable string
 */
var simplifyString = function(str){
  return str
    .trim()
    .toLowerCase()
    .replace(/[\t'\-! ()]/g, '')
    .replace(/ +/g, ' ')
    .replace(/[ًٌٍَُِّْـ.]/g, '')
    .replace(/ي/g, 'ی')
    .replace(/ى/g, 'ی')
    .replace(/ك/g, 'ک')
    .replace(/[àâä]/g, 'a')
    .replace(/æ/g, 'ae')
    .replace(/[îï]/g, 'i')
    .replace(/[éèêë]/g, 'e')
    .replace(/[ôö]/g, 'o')
    .replace(/œ/g, 'oe')
    .replace(/[ùûü]/g, 'u')
    .replace(/ç/g, 'c')
    .replace(/ñ/g, 'n')
    .replace(/ÿ/g, 'y')
    ;
};

/* list subscriptions of the current user */
router.get('/me', authenticate, access('subscription:get-me', 'کاربر سابقه اشتراک‌های خود را مشاهده کند'), async (req, res, next) => {
  if(!req.user || !req.isAuthenticated()) return fail(res, null, consts.e.ERR_AUTH)();

  let conn;
  try {
    conn = await db.getConnectionPromise();
  }catch(err) { return fail(res, conn, consts.e.ERR_DB_ERROR)(); }

  var query = {};

  query.user = req.user.field('email');
  if(req.query.hasOwnProperty('book')) query.book = parseInt(req.query.book);
  if(req.query.hasOwnProperty('record')) query.record = parseInt(req.query.record);
  if(req.query.hasOwnProperty('minDate')) query['>=dateExpire'] = req.query.minDate;
  if(req.query.hasOwnProperty('date')) {
    query['>=dateExpire'] = req.query.date;
    query['<=dateStart'] = req.query.date;
  }
  if(req.query.hasOwnProperty('maxDate')) query['<=dateStart'] = req.query.maxDate;

  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;

  try {
    let records = await SubscriptionModel.list(conn, query, limit, offset, req.query.orderBy);
    // list of records based on criteria
    let response = [];
    let sum = await SubscriptionModel.count(conn, query)
      records.forEach(record => {
        delete record.user_searchable;
        delete record.user_sortable;
        delete record.created;
        delete record.id;
        delete record.record;
        delete record.source_id;
        delete record.user_id;
      });
      send(res, conn, {sum: sum, list: records});
  }catch(e) {
    fail(res, conn, consts.e.ERR_DB_ERROR)();
  }
});

/* load a subscription by id */
router.get('/:id', authenticate, access('subscription:get-id', 'نمایش فهرست اشتراک‌های یک کاربر'), function(req, res, next){
  if(!req.params.id) return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)('id is required.');

  reqid = parseInt(req.params.id);
  if(isNaN(reqid)) return fail(res, null, consts.e.ERR_MALFORMED_REQUEST)('requested id is not a number.');

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

    var subscriptionModel = new SubscriptionModel(conn, {id: reqid});
    subscriptionModel.load().then(
      function(subscription){
        if(!subscription) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();
        send(res, conn, subscription.fields());
      }, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

/* list all subscriptions */
router.get('/', authenticate, access('subscription:get', 'نمایش فهرست اشتراک‌ها'), function(req, res, next){
  db.getConnection(function(err, conn){
    if(err) return fail(res, conn, err)();

    var query = {};

    if(req.query.hasOwnProperty('user')) query.user = req.query.user;
    if(req.query.hasOwnProperty('book')) query.book = req.query.book;
    if(req.query.hasOwnProperty('record')) query.record = parseInt(req.query.record);
    if(req.query.hasOwnProperty('state')) query.state = parseInt(req.query.state);
    if(req.query.hasOwnProperty('minDate')) query['>=dateExpire'] = req.query.minDate;
    if(req.query.hasOwnProperty('date')) {
      query['>=dateExpire'] = req.query.date;
      query['<=dateStart'] = req.query.date;
    }
    if(req.query.hasOwnProperty('maxDate')) query['<=dateStart'] = req.query.maxDate;

    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;

    if(req.query.hasOwnProperty('displayname'))
      query['%%user_searchable'] = '%'+simplifyString(req.query.displayname)+'%';

    if(req.query.hasOwnProperty('user') && (/^%/.exec(req.query.user) || /%$/.exec(req.query.user))){
      delete query.user;
      query['%%user'] = simplifyString(req.query.user);
    }

    // list of records based on criteria
    var listQuery = function(records) {
      var response = [];
      return SubscriptionModel.count(conn, query).then(
        function(sum) {
          send(res, conn, {sum: sum, list: records});
        }, fail(res, conn, consts.e.ERR_DB_ERROR));
    };

    SubscriptionModel.list(conn, query, limit, offset, req.query.orderBy).then(
      listQuery, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

/* add a new subscription record */
router.put('/', authenticate, access('subscription:put', 'افزودن اشتراک'), function(req, res, next) {
  if(!req.body.user || !req.body.book || !req.body.dateStart || !req.body.dateExpire)
    return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)();

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

    (new UserModel(conn, {email: req.body.user})).load().then(
      function(user) {
        if(!user) return fail(res, conn, consts.ERR_MISSING_RECORD)();

        (new DictionaryModel(conn, {uuid: req.body.book})).load().then(
          function(book) {
            if(!book) return fail(res, conn, consts.ERR_MISSING_RECORD)();

            var data = {
              user: user.field('id'),
              book: book.field('id'),
              dateStart: req.body.dateStart,
              dateExpire: req.body.dateExpire,
            }

            if(req.body.record)
              data.record = parseInt(req.body.record);

            if(req.body.state)
              data.state = parseInt(req.body.state);

            var subscriptionModel = new SubscriptionModel(conn, data);
            subscriptionModel.save().then(
              function(subscription) {
                subscription.load().then(
                  function() {
                    send(res, conn, subscription.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));
      }, fail(res, conn, consts.e.ERR_DB_ERROR));
  });
});

/* update an existing subscription record */
router.post('/:id', authenticate, access('subscription:post-id', 'ویرایش اطلاعات اشتراک'), function(req, res, next) {
  if(!req.params.id)
    return fail(res, null, consts.e.ERR_REQUIRED_FIELDS)();

  reqid = parseInt(req.params.id);
  if(isNaN(reqid)) return fail(res, null, consts.e.ERR_MALFORMED_REQUEST)('requested id is not a number.');

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

    var subscriptionModel = new SubscriptionModel(conn, {id: reqid});
    subscriptionModel.load(true).then(
      function(subscription){
        if(!subscription) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();

        // ignore user and book and record from request. only dates and state may be updated
        if(req.body.hasOwnProperty('dateStart')) subscription.field('dateStart', req.body.dateStart);
        if(req.body.hasOwnProperty('dateExpire')) subscription.field('dateExpire', req.body.dateExpire);
        if(req.body.hasOwnProperty('state')) subscription.field('state', parseInt(req.body.state));

        subscription.save().then(
          function(subscription){
            req.body.id = parseInt(req.params.id);
            send(res, conn, req.body);
          }, fail(res, conn, consts.e.ERR_DB_ERROR));
      }, fail(res, conn, consts.e.ERR_DB_ERROR)
    );
  });
});

/* delete a subscription record */
router.delete('/:id', authenticate, access('subscription:delete-id', 'حذف اشتراک'), 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)();

    var subscriptionModel = new SubscriptionModel(conn, {id: parseInt(req.params.id)});
    subscriptionModel.load(true).then(
      function(subscription){
        if(!subscription) return fail(res, conn, consts.e.ERR_MISSING_RECORD)();

        subscription.delete().then(
          function(){
            send(res, conn, {id: req.params.id});
          }, fail(res, conn, consts.e.ERR_DB_ERROR));
      }, fail(res, conn, consts.e.ERR_DB_ERROR)
    );
  });
});

module.exports = router;