├── .env.example
├── .eslintrc.json
├── .gitignore
├── .npmrc
├── .vscode
└── settings.json
├── README.md
├── app.js
├── bin
└── server.js
├── database
├── connection.js
├── extra
│ └── defualtColumns.js
├── knexfile.js
├── migrations
│ ├── 20200709151446_createTestTable.js
│ ├── 20200719140110_option.js
│ ├── 20201022110529_user.js
│ ├── 20201022111144_user_transaction.js
│ ├── 20201022111643_payment_medium.js
│ ├── 20201022111928_transaction_type.js
│ ├── 20201022113834_job.js
│ ├── 20201022115535_page.js
│ ├── 20210110234212_add_rate_to_job.js
│ ├── 20210115180408_add_transaction_confirmed.js
│ ├── 20210121100727_add_currency_to_paymentmedium.js
│ ├── 20210121102927_activation_token.js
│ ├── 20210126112419_add_columns_to_user.js
│ ├── 20210126113022_add_from_api_to_job.js
│ └── 20210129095447_add_callback_to_job.js
└── seeds
│ ├── option.js
│ ├── payment_medium.js
│ └── transaction_type.js
├── faq.js
├── firebase
└── draft.js
├── helpers
├── aws
│ ├── config.js
│ ├── pipeS3ObjectToExpress.js
│ ├── putObjects.js
│ └── removeObjects.js
├── convertNumberToArabic.js
├── email.js
├── emailTemplates.js
├── isValidAPIKey.js
├── numberWithCommas.js
├── options.js
├── ratelimit.js
└── superagent.js
├── middlewares
├── jwt
│ └── user.js
└── validators
│ ├── auth.js
│ ├── blockedMailProviders.js
│ ├── common
│ ├── dataGrid.js
│ ├── lazyLoad.js
│ ├── searchQuery.js
│ └── storage.js
│ ├── data
│ ├── idPay.js
│ ├── job.js
│ └── user.js
│ ├── fastpay.js
│ ├── idPay.js
│ ├── job.js
│ ├── user.js
│ └── validate.js
├── package-lock.json
├── package.json
├── query
├── job.js
└── user.js
└── routes
├── auth.js
├── fastpay.js
├── idPay.js
├── index.js
├── job.js
├── s3interface.js
└── user.js
/.env.example:
--------------------------------------------------------------------------------
1 | # enviroment variables goes here
2 | USER_JWT_SECRET = "123"
3 | NODE_ENV = "development"
4 | # currenrt domain
5 | DOMAIN = "https://zhir.io"
6 |
7 | #database connection info
8 | HOST ="localhost"
9 | DB_USER ="root"
10 | DB_NAME = "zhir-ocr"
11 | DB_PASSWORD = "123"
12 |
13 | #email configuration
14 | MAILING_LIST = ""
15 | SENDER_EMAIL = "aramrafeq2@gamil.com"
16 | SENDER_PASSWORD = "123"
17 |
18 | # s3 bucket configuration
19 | AWS_ENDPOINT= ""
20 | AWS_ACCESS_KEY_ID= ""
21 | AWS_SECRET_ACCESS_KEY = ""
22 | AWS_BUCKET = ""
23 |
24 | # ID Pay ENV variables
25 | IDPAY_AUTH_KEY = "121312"
26 | IDPAY_SANDBOX = "1"
27 |
28 | #Fastpay
29 | FASTPAY_API_URL = "https://apigw-merchant.fast-pay.iq"
30 | FASTPAY_STORE_ID = "21312312"
31 | FASTPAY_MERCHANT_STORE_PASS = "$$"
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "commonjs": true,
4 | "es2021": true,
5 | "node": true
6 | },
7 | "extends": [
8 | "airbnb-base"
9 | ],
10 | "parserOptions": {
11 | "ecmaVersion": 12
12 | },
13 | "rules": {
14 | "linebreak-style": "off",
15 | "no-tabs": "off",
16 | "indent": ["error", "tab", { "SwitchCase": 1 }]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | .env
3 | /log
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | scripts-prepend-node-path=true
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": false,
3 | "editor.codeActionsOnSave": {
4 | "source.fixAll.eslint": true
5 | },
6 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # API
3 | This acts as the main rest api for zhir.io
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const debug = require('debug');
3 | const morgan = require('morgan');
4 | const helmet = require('helmet');
5 | const cors = require('cors');
6 | const compression = require('compression');
7 | const rfs = require('rotating-file-stream');
8 | const path = require('path');
9 | require('dotenv').config();
10 |
11 | const app = express();
12 | const accessLogStream = rfs.createStream('access.log', {
13 | size: '10M', // rotate every 10 MegaBytes written
14 | interval: '1d', // rotate daily
15 | path: path.join(__dirname, 'log'),
16 | });
17 |
18 | let loggerOptions = {
19 | stream: accessLogStream,
20 | };
21 | if (process.env.NODE_ENV === 'development') {
22 | loggerOptions = {};
23 | }
24 | const logger = morgan('combined', loggerOptions);
25 | app.use(logger);
26 |
27 | app.use(cors());
28 | // app.use(helmet({
29 | // contentSecurityPolicy: false,
30 | // }));
31 | app.use(helmet());
32 | app.use(compression());
33 |
34 | app.use(express.json({ limit: '80mb' }));
35 | app.set('view engine', 'ejs');
36 |
37 | const seInterfaceRouter = require('./routes/s3interface');
38 |
39 | app.use('/files', seInterfaceRouter);
40 |
41 | app.use('/', require('./routes'));
42 |
43 | app.get('/', (req, res) => {
44 | res.json({ msg: 'Zhir.io API' });
45 | });
46 | // eslint-disable-next-line no-unused-vars
47 | app.use((err, req, res, next) => {
48 | debug.error(err.stack);
49 | res.status(500).send(err.toString());
50 | });
51 |
52 | module.exports = app;
53 |
--------------------------------------------------------------------------------
/bin/server.js:
--------------------------------------------------------------------------------
1 | const debug = require('debug');
2 |
3 | const app = require('../app');
4 |
5 | const port = process.env.PORT || 3000;
6 | app.listen(port, () => {
7 | debug.log(`app listening at http://localhost:${port}`);
8 | });
9 |
--------------------------------------------------------------------------------
/database/connection.js:
--------------------------------------------------------------------------------
1 | const db = require('knex');
2 | const path = require('path');
3 |
4 | require('dotenv').config({ path: path.join(__dirname, '../../../', '.env') });
5 |
6 | module.exports = db({
7 | client: 'mysql',
8 | connection: {
9 | host: process.env.HOST,
10 | user: process.env.DB_USER,
11 | password: process.env.DB_PASSWORD,
12 | database: process.env.DB_NAME,
13 | debug: false,
14 | },
15 | port: 80,
16 | });
17 |
--------------------------------------------------------------------------------
/database/extra/defualtColumns.js:
--------------------------------------------------------------------------------
1 | module.exports = (table, excludes = []) => {
2 | if (excludes.indexOf('active') < 0) {
3 | table.boolean('active').notNullable().defaultTo(1);
4 | }
5 | if (excludes.indexOf('deleted') < 0) {
6 | table.boolean('deleted').notNullable().defaultTo(0);
7 | }
8 | if (excludes.indexOf('updated_at') < 0) {
9 | table.datetime('updated_at', { precision: 6 });
10 | table.integer('updated_by').notNullable().defaultTo(0);
11 | }
12 |
13 | table.datetime('created_at', { precision: 6 });
14 | table.integer('created_by').notNullable().defaultTo(0);
15 | table.charset('utf8mb4');
16 | return table;
17 | };
18 |
--------------------------------------------------------------------------------
/database/knexfile.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | require('dotenv').config({ path: path.join(__dirname, '../../', '.env') });
3 |
4 | const folders = {
5 | seeds: {
6 | directory: './seeds',
7 | },
8 | migrations: {
9 | directory: './migrations',
10 | },
11 | };
12 |
13 | module.exports = {
14 | development: {
15 | client: 'mysql',
16 | connection: {
17 | host: process.env.HOST,
18 | user: process.env.DB_USER,
19 | password: process.env.DB_PASSWORD,
20 | database: process.env.DB_NAME,
21 | debug: false,
22 | },
23 | port: 80,
24 | ...folders,
25 | },
26 | production: {
27 | client: 'mysql',
28 | connection: {
29 | host: process.env.HOST,
30 | user: process.env.DB_USER,
31 | password: process.env.DB_PASSWORD,
32 | database: process.env.DB_NAME,
33 | debug: false,
34 | },
35 | port: 80,
36 | ...folders,
37 | },
38 | };
39 |
--------------------------------------------------------------------------------
/database/migrations/20200709151446_createTestTable.js:
--------------------------------------------------------------------------------
1 | exports.up = (knex) =>
2 | knex.schema.createTable('test', (table) => {
3 | table.increments('id').primary();
4 | table.string('name', 350).notNullable().defaultTo('default name');
5 | table.charset('utf8mb4');
6 | // table.collate('utf8_general_ci');
7 | });
8 |
9 | exports.down = (knex) => knex.schema.dropTable('test');
10 |
--------------------------------------------------------------------------------
/database/migrations/20200719140110_option.js:
--------------------------------------------------------------------------------
1 | exports.up = (knex) =>
2 | knex.schema.createTable('option', (table) => {
3 | table.increments('id').primary();
4 | table.string('key', 355).notNullable().defaultTo('');
5 | table.string('value', 355).notNullable().defaultTo('');
6 | table.charset('utf8mb4');
7 | });
8 |
9 | exports.down = (knex) => knex.schema.dropTable('option');
10 |
--------------------------------------------------------------------------------
/database/migrations/20201022110529_user.js:
--------------------------------------------------------------------------------
1 | const defualts = require('../extra/defualtColumns');
2 |
3 | exports.up = (knex) => knex.schema.createTable('user', (table) => {
4 | table.increments('id').primary();
5 | table.string('name', 350).notNullable().defaultTo('');
6 | table.string('company_name', 350).notNullable().defaultTo('');
7 | table.string('email', 350).notNullable().unique();
8 | table.string('password', 100).notNullable().defaultTo('');
9 | table.string('tmp_password', 100).defaultTo('');
10 | table.string('activation_token', 100).defaultTo('');
11 | table.string('salt', 100).notNullable().defaultTo('');
12 | table
13 | .enu('gender', ['male', 'female', 'unspecified'])
14 | .notNullable()
15 | .defaultTo('unspecified');
16 | table.boolean('is_admin').defaultTo(0);
17 | table.boolean('verified').defaultTo(0);
18 | table.date('birthdate');
19 | table.string('phone_no', 100).notNullable().defaultTo('');
20 | table.string('uid', 200).notNullable().defaultTo('');
21 | defualts(table);
22 | table.charset('utf8mb4');
23 | });
24 |
25 | exports.down = (knex) => knex.schema.dropTable('user');
26 |
--------------------------------------------------------------------------------
/database/migrations/20201022111144_user_transaction.js:
--------------------------------------------------------------------------------
1 | const defualts = require('../extra/defualtColumns');
2 |
3 | exports.up = (knex) =>
4 | knex.schema.createTable('user_transaction', (table) => {
5 | table.increments('id').primary();
6 | table.integer('user_id');
7 | table.integer('type_id');
8 | table.integer('payment_medium_id').defaultTo(0); // defualt payment medium we can add extra column to payment_mediums
9 | table.integer('page_count');
10 | table.decimal('amount', 11, 3).notNullable().defaultTo(1000); // we use IQD only
11 | table.string('transaction_id', 350);
12 | table.string('user_note', 350).defaultTo('');
13 | table.string('admin_note', 350).defaultTo('');
14 |
15 | defualts(table, ['active', 'deleted', 'updated_at']);
16 | table.charset('utf8mb4');
17 | });
18 |
19 | exports.down = (knex) => knex.schema.dropTable('user_transaction');
20 |
--------------------------------------------------------------------------------
/database/migrations/20201022111643_payment_medium.js:
--------------------------------------------------------------------------------
1 | const defualts = require('../extra/defualtColumns');
2 |
3 | exports.up = (knex) =>
4 | knex.schema.createTable('payment_medium', (table) => {
5 | table.increments('id').primary();
6 | table.string('name', 350).notNullable().defaultTo('');
7 | table.string('code', 350).notNullable().defaultTo('');
8 | table.decimal('min_amount', 11, 3).notNullable().defaultTo(0); // minimum amout to use for a transaction
9 | table.decimal('max_amount', 11, 3).notNullable().defaultTo(0); // maximum amout to use for a transaction
10 | defualts(table);
11 | table.charset('utf8mb4');
12 | });
13 |
14 | exports.down = (knex) => knex.schema.dropTable('payment_medium');
15 |
--------------------------------------------------------------------------------
/database/migrations/20201022111928_transaction_type.js:
--------------------------------------------------------------------------------
1 | exports.up = (knex) =>
2 | knex.schema.createTable('transaction_type', (table) => {
3 | table.increments('id').primary();
4 | table.string('name', 350).notNullable().defaultTo('');
5 | table.string('code', 350).notNullable().defaultTo('');
6 | table.charset('utf8mb4');
7 | });
8 |
9 | exports.down = (knex) => knex.schema.dropTable('transaction_type');
10 |
--------------------------------------------------------------------------------
/database/migrations/20201022113834_job.js:
--------------------------------------------------------------------------------
1 | const defualts = require('../extra/defualtColumns');
2 |
3 | exports.up = (knex) =>
4 | knex.schema.createTable('job', (table) => {
5 | table.string('id', 36).primary();
6 | table.string('name', 350).notNullable().defaultTo('');
7 | table.string('code', 350).notNullable().defaultTo('');
8 | table.integer('user_id');
9 | table.integer('page_count');
10 | table.integer('paid_page_count');
11 | table.string('user_failing_reason', 350).notNullable().defaultTo('');
12 | table.enu('status', [
13 | 'pending',
14 | 'queued',
15 | 'processing',
16 | 'completed',
17 | 'failed',
18 | ]);
19 | table.string('lang', 350).defaultTo('ckb');
20 |
21 | table.datetime('queued_at', { precision: 6 }).defaultTo(knex.fn.now(6));
22 | table.datetime('processed_at', { precision: 6 });
23 |
24 | table.datetime('finished_at', { precision: 6 });
25 | // table.datetime('failed_at', { precision: 6 });
26 | table.string('failing_reason', 450).notNullable().defaultTo('');
27 |
28 | defualts(table, ['active', 'updated_at']);
29 | table.charset('utf8mb4');
30 | });
31 |
32 | exports.down = (knex) => knex.schema.dropTable('job');
33 |
--------------------------------------------------------------------------------
/database/migrations/20201022115535_page.js:
--------------------------------------------------------------------------------
1 | const defualts = require('../extra/defualtColumns');
2 |
3 | exports.up = (knex) =>
4 | knex.schema.createTable('page', (table) => {
5 | table.string('id', 36).primary();
6 | table.string('name', 350).notNullable().defaultTo('');
7 | table.integer('user_id');
8 | table.string('job_id', 350);
9 | table
10 | .datetime('started_processing_at', { precision: 6 })
11 | .defaultTo(knex.fn.now(6));
12 |
13 | table.boolean('processed').notNullable().defaultTo(0);
14 | table.boolean('is_free').notNullable().defaultTo(0);
15 |
16 | table
17 | .datetime('finished_processing_at', { precision: 6 })
18 | .defaultTo(knex.fn.now(6));
19 | table.boolean('succeeded').notNullable().defaultTo(1);
20 |
21 | table.text('result', 'MEDIUMTEXT');
22 | table.text('processed_result', 'MEDIUMTEXT');
23 |
24 | defualts(table, ['active', 'updated_at']);
25 | table.charset('utf8mb4');
26 | });
27 |
28 | exports.down = (knex) => knex.schema.dropTable('page');
29 |
--------------------------------------------------------------------------------
/database/migrations/20210110234212_add_rate_to_job.js:
--------------------------------------------------------------------------------
1 | exports.up = (knex) =>
2 | knex.schema.alterTable('job', (table) => {
3 | table.integer('rate', 11).defaultTo(-1000);
4 | });
5 |
6 | exports.down = (knex) =>
7 | knex.schema.alterTable('job', (table) => {
8 | table.dropColumn('rate');
9 | });
10 |
--------------------------------------------------------------------------------
/database/migrations/20210115180408_add_transaction_confirmed.js:
--------------------------------------------------------------------------------
1 | exports.up = (knex) =>
2 | knex.schema.alterTable('user_transaction', (table) => {
3 | table.boolean('confirmed').defaultTo(0);
4 | });
5 |
6 | exports.down = (knex) =>
7 | knex.schema.alterTable('user_transaction', (table) => {
8 | table.dropColumn('confirmed');
9 | });
10 |
--------------------------------------------------------------------------------
/database/migrations/20210121100727_add_currency_to_paymentmedium.js:
--------------------------------------------------------------------------------
1 | exports.up = (knex) =>
2 | knex.schema.alterTable('payment_medium', (table) => {
3 | table.string('currency_symbol', 100).defaultTo('');
4 | });
5 |
6 | exports.down = (knex) =>
7 | knex.schema.alterTable('payment_medium', (table) => {
8 | table.dropColumn('currency_symbol');
9 | });
10 |
--------------------------------------------------------------------------------
/database/migrations/20210121102927_activation_token.js:
--------------------------------------------------------------------------------
1 | const defualts = require('../extra/defualtColumns');
2 |
3 | exports.up = (knex) =>
4 | knex.schema.createTable('activation_token', (table) => {
5 | table.increments('id').primary();
6 | table.integer('user_id');
7 | table.string('token', 350).notNullable().defaultTo('');
8 |
9 | defualts(table, ['active', 'updated_at']);
10 | table.charset('utf8mb4');
11 | });
12 |
13 | exports.down = (knex) => knex.schema.dropTable('activation_token');
14 |
--------------------------------------------------------------------------------
/database/migrations/20210126112419_add_columns_to_user.js:
--------------------------------------------------------------------------------
1 | exports.up = (knex) =>
2 | knex.schema.alterTable('user', (table) => {
3 | table.boolean('can_use_api').defaultTo(0);
4 | table.string('api_key', 200).defaultTo('');
5 | table.integer('monthly_recharge', 11).defaultTo(50);
6 | });
7 |
8 | exports.down = (knex) =>
9 | knex.schema.alterTable('user', (table) => {
10 | table.dropColumn('can_use_api');
11 | table.dropColumn('api_key');
12 | table.dropColumn('monthly_recharge');
13 | });
14 |
--------------------------------------------------------------------------------
/database/migrations/20210126113022_add_from_api_to_job.js:
--------------------------------------------------------------------------------
1 | exports.up = (knex) =>
2 | knex.schema.alterTable('job', (table) => {
3 | table.boolean('from_api').defaultTo(0);
4 | });
5 |
6 | exports.down = (knex) =>
7 | knex.schema.alterTable('job', (table) => {
8 | table.dropColumn('from_api');
9 | });
10 |
--------------------------------------------------------------------------------
/database/migrations/20210129095447_add_callback_to_job.js:
--------------------------------------------------------------------------------
1 | exports.up = (knex) =>
2 | knex.schema.alterTable('job', (table) => {
3 | table.string('callback', 1000).defaultTo('');
4 | });
5 |
6 | exports.down = (knex) =>
7 | knex.schema.alterTable('job', (table) => {
8 | table.dropColumn('callback');
9 | });
10 |
--------------------------------------------------------------------------------
/database/seeds/option.js:
--------------------------------------------------------------------------------
1 | exports.seed = (knex) =>
2 | knex('option')
3 | .del()
4 | .then(() =>
5 | knex('option').insert([
6 | {
7 | key: 'price_per_page',
8 | value: 150,
9 | },
10 | ])
11 | );
12 |
--------------------------------------------------------------------------------
/database/seeds/payment_medium.js:
--------------------------------------------------------------------------------
1 | exports.seed = (knex) =>
2 | knex('payment_medium')
3 | .del()
4 | .then(() =>
5 | knex('payment_medium').insert([
6 | {
7 | id: 1,
8 | name: 'ژیر',
9 | code: 'ZHIR',
10 | min_amount: 1000, // 1000IQD
11 | max_amount: 350000, // 350000 IQD
12 | currency_symbol: '',
13 | },
14 | {
15 | id: 2,
16 | name: 'فاستپەی',
17 | code: 'FASTPAY',
18 | min_amount: 1000, // 1000IQD
19 | max_amount: 350000, // 350000 IQD
20 | currency_symbol: 'د.ع',
21 | },
22 | {
23 | id: 3,
24 | name: 'ئاسیا حەواڵە',
25 | code: 'ASIA_HAWALA',
26 | min_amount: 1000, // 1000IQD
27 | max_amount: 350000, // 350000 IQD
28 | currency_symbol: 'د.ع',
29 | },
30 | {
31 | id: 4,
32 | name: 'زەین کاش',
33 | code: 'ZAIN_CASH',
34 | min_amount: 1000, // 1000IQD
35 | max_amount: 350000, // 350000 IQD
36 | currency_symbol: 'د.ع',
37 | },
38 | {
39 | id: 5,
40 | name: 'باڵانس',
41 | code: 'BALANCE',
42 | min_amount: 0, // 1000IQD
43 | max_amount: 350000, // 350000 IQD
44 | currency_symbol: 'د.ع',
45 | },
46 | {
47 | id: 6,
48 | name: 'ئایدی پەی',
49 | code: 'IDPAY',
50 | min_amount: 0,
51 | max_amount: 3500000,
52 | currency_symbol: 'ریال',
53 | },
54 | ])
55 | );
56 |
--------------------------------------------------------------------------------
/database/seeds/transaction_type.js:
--------------------------------------------------------------------------------
1 | exports.seed = (knex) =>
2 | knex('transaction_type')
3 | .del()
4 | .then(() =>
5 | knex('transaction_type').insert([
6 | {
7 | id: 1,
8 | name: 'پرکرنەوەی باڵانس',
9 | code: 'RECHARGE',
10 | },
11 | {
12 | id: 2,
13 | name: 'سکانکردن',
14 | code: 'OCR-JOB',
15 | },
16 | {
17 | id: 3,
18 | name: 'ناردنی باڵانس',
19 | code: 'BALANCE-TRANSFER',
20 | },
21 | ])
22 | );
23 |
--------------------------------------------------------------------------------
/faq.js:
--------------------------------------------------------------------------------
1 | const faqs = [
2 | {
3 | question: 'ژیر بە خۆڕاییە؟',
4 | answer:
5 | 'نەخێر، لە خزمەتگوزاری ژیر دەتوانیت بەپێی پێویستی پەرە بکڕیت و دوای ئەوە هەرکاتێک ویستت بەکاریان بهێنیت بۆ دەرهێنانی دەقەکان.',
6 | },
7 | {
8 | question: 'ژیر دەستنووس دەناسێتەوە؟',
9 | answer:
10 | 'نەخێر، تەنها دۆکیۆمێنتی پرینتکراو یان سکانکراو دەکاتەوە بە نووسین لە داهاتوودا دەستنووسیش دەناسێتەوە.',
11 | },
12 | {
13 | question: 'تاوەکو چەند زانیاریەکانم پارێزراون؟',
14 | answer:
15 | 'فایل و زانیاریەکانت لەسەر راژەیەکی تایبەتی هەڵدەگیرێت کە کەس ناتوانێت دەستی پێیان بگات.',
16 | },
17 | {
18 | question: 'نووسینی تێکەڵ دەناسێتەوە بە چەند زمانێک نوسرابێت؟',
19 | answer:
20 | 'وا باشترە پەڕەی سکانکراو تەنها کوردی یان عەرەبی تێدابێت بۆ ئەوەی بەباشی دەقەکان دەربهێنێت، نوسینی ئینگلیزی دەناسێتەوە بەڵام لەوانەیە کاریگەری هەبێت لەسەر دروستی دەقی دەرهێندراو هەرچەندە ژمارە و رێکەوتی ئینگلیزی کاریگەری نیە و ئاساییە.',
21 | },
22 | {
23 | question: 'ژیر هەموو شتێک بە دروستی دەردەهێنێت؟',
24 | answer:
25 | 'تا ڕادەیەک، هەرچەندە هەموو هەوڵی خۆمان داوە بۆ ئەوەی بە دروستترین شێوە، نووسین و دەقەکان بناسێتەوە بەردەوامیشین لە هەوڵدان بۆ باشترکردنی.',
26 | },
27 | {
28 | question:
29 | 'پێویست دەکا ماڵپەری ژیر بە کراوەیی بهێڵمەوە لەکاتی جێبەجێکردنی کارەکان؟',
30 | answer:
31 | 'نەخێر. دوای ئەوەی وێنەکانت بارکرد، دەتوانی ماڵپەری ژیر دابخەی. ژیر کارەکان لەسەر سێرڤەر دەکات. دەتوانی هەر کات ویستت لە لیستی ناسینەوەی وێنە ئەنجامەکان وەربگریەوە.',
32 | },
33 | {
34 | question: 'چۆن پەیوەندی بە ژیرەوە بکەین؟',
35 | answer:
36 | 'دەتوانن ڕەخنە و پێشنیارەکانتان بنێرن بۆ پۆستی ئەلیکترۆنی ژیر info@zhir.io یان لەرێگای چاتی ڕاستەوخۆ.',
37 | },
38 | ];
39 | export default faqs;
40 |
--------------------------------------------------------------------------------
/firebase/draft.js:
--------------------------------------------------------------------------------
1 | function initApp() {
2 | firebase.auth().onAuthStateChanged(function (user) {
3 | if (user) {
4 | // User is signed in.
5 | // we dont care about signed in user
6 | // when user signs in we create custom session from out bakcend using idToken
7 | user.getIdToken().then(function (idToken) {
8 | // <------ Check this line
9 | console.log(idToken); // It shows the Firebase token now
10 | });
11 | console.log(user);
12 | } else {
13 | // User is signed out.
14 | alert("user signed out");
15 | }
16 | });
17 | }
18 | let logout = () => {
19 | firebase.auth().signOut();
20 | };
21 | let signInWithGoogle = () => {
22 | var provider = new firebase.auth.GoogleAuthProvider();
23 | firebase
24 | .auth()
25 | .signInWithPopup(provider)
26 | .then((result) => {
27 | console.log(result.credential);
28 | })
29 | .catch((error) => {
30 | var errorCode = error.code;
31 | var errorMessage = error.message;
32 | // The email of the user's account used.
33 | var email = error.email;
34 | // The firebase.auth.AuthCredential type that was used.
35 | var credential = error.credential;
36 | console.log(error);
37 | });
38 | };
39 |
40 | onMount(async () => {
41 | initApp();
42 | });
43 | {
44 | /*
45 | */
46 | }
47 |
--------------------------------------------------------------------------------
/helpers/aws/config.js:
--------------------------------------------------------------------------------
1 | const AWS = require('aws-sdk');
2 |
3 | AWS.config.update({
4 | endpoint: process.env.AWS_ENDPOINT,
5 | accessKeyId: process.env.AWS_ACCESS_KEY_ID,
6 | secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
7 | });
8 |
9 | const s3 = new AWS.S3();
10 |
11 | module.exports = {
12 | AWS,
13 | s3,
14 | };
15 |
--------------------------------------------------------------------------------
/helpers/aws/pipeS3ObjectToExpress.js:
--------------------------------------------------------------------------------
1 | const { s3 } = require('./config');
2 |
3 | module.exports = (params, res) => {
4 | s3.headObject(params, (err, headers) => {
5 | if (err) {
6 | res.status(404).json({
7 | msg: 'ئەنجامی داواکراو بوونی نیە یان نەدۆزرایەوە',
8 | });
9 | } else {
10 | const stream = s3.getObject(params).createReadStream();
11 |
12 | stream.on('error', () => {
13 | res.status(404).json({
14 | msg: 'هەڵەیەک رویدا لەکاتی ناردنەوەی ئەنجام',
15 | });
16 | });
17 | res.set('Content-Type', headers.ContentType);
18 | res.set('Content-Length', headers.ContentLength);
19 | res.set('Last-Modified', headers.LastModified);
20 | res.set('ETag', headers.ETag);
21 | // Pipe the s3 object to the response
22 | stream.pipe(res);
23 | }
24 | });
25 | };
26 |
--------------------------------------------------------------------------------
/helpers/aws/putObjects.js:
--------------------------------------------------------------------------------
1 | const { s3 } = require('./config');
2 |
3 | module.exports = (objects = []) => {
4 | /*
5 | example of a single object should be something like
6 | for reguller ocr images the convention is here
7 | userid/jobid/index.jpg
8 | {
9 | index: 0,
10 | name: f.filename,
11 | base64: f.getFileEncodeDataURL(),
12 | type: "application/pdf",
13 | extension: "jpg",
14 | path: `/original/`,
15 | }
16 | */
17 | const objectPromises = [];
18 | // const today = new Date();
19 | // const year = today.getFullYear();
20 | // const month = today.toLocaleString('default', { month: 'long' });
21 | objects.forEach((object) => {
22 | objectPromises.push(
23 | new Promise((resolve, reject) => {
24 | const base64String = object.base64.substr(
25 | object.base64.indexOf(';base64,') + 8,
26 | object.length,
27 | );
28 | // eslint-disable-next-line new-cap
29 | const binaryData = new Buffer.from(base64String, 'base64');
30 | s3.upload(
31 | {
32 | Bucket: `${process.env.AWS_BUCKET}/${object.path}`, // process.env.AWS_BUCKET
33 | Body: binaryData,
34 | // Key: `${uid.time()}-${object.name}`,
35 | // Key: `${uuiv4()}`,
36 | Metadata: {
37 | name: Buffer.from(`${object.name}`).toString(
38 | 'base64',
39 | ),
40 | extention: `${object.extention}`,
41 | index: `${object.index}`,
42 | },
43 | Key: `${object.index}.${object.extention}`,
44 | // ACL: 'public-read', // this should change in the future
45 | ContentType: object.type,
46 | CacheControl: 'max-age=31557600',
47 | },
48 | (err, data) => (err == null ? resolve(data) : reject(err)),
49 | );
50 | }),
51 | );
52 | });
53 | return Promise.all(objectPromises);
54 | };
55 |
--------------------------------------------------------------------------------
/helpers/aws/removeObjects.js:
--------------------------------------------------------------------------------
1 | const { s3 } = require('./config');
2 |
3 | module.exports = async (keys = []) => {
4 | const deleteKeys = keys.map((k) => ({
5 | Key: k,
6 | }));
7 | const params = {
8 | Bucket: `${process.env.AWS_BUCKET}`,
9 | Delete: {
10 | Objects: deleteKeys,
11 | Quiet: true,
12 | },
13 | };
14 | return s3.deleteObjects(params, (err, data) => {
15 | if (err) {
16 | return Promise.reject(err);
17 | }
18 |
19 | return Promise.resolve(data);
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/helpers/convertNumberToArabic.js:
--------------------------------------------------------------------------------
1 | module.exports = (rawNumber) => {
2 | const number = `${rawNumber}`;
3 | let charIndex = 0;
4 | let NumericArabic = '';
5 |
6 | while (charIndex < number.length) {
7 | switch (number[charIndex]) {
8 | case '.':
9 | NumericArabic += '.';
10 | break;
11 |
12 | case '0':
13 | NumericArabic += '٠';
14 | break;
15 |
16 | case '1':
17 | NumericArabic += '١';
18 | break;
19 |
20 | case '2':
21 | NumericArabic += '٢';
22 | break;
23 |
24 | case '3':
25 | NumericArabic += '٣';
26 | break;
27 |
28 | case '4':
29 | NumericArabic += '٤';
30 | break;
31 |
32 | case '5':
33 | NumericArabic += '٥';
34 | break;
35 |
36 | case '6':
37 | NumericArabic += '٦';
38 | break;
39 |
40 | case '7':
41 | NumericArabic += '٧';
42 | break;
43 |
44 | case '8':
45 | NumericArabic += '٨';
46 | break;
47 |
48 | case '9':
49 | NumericArabic += '٩';
50 | break;
51 |
52 | default:
53 | NumericArabic += number[charIndex];
54 | break;
55 | }
56 |
57 | charIndex += 1;
58 | }
59 |
60 | return NumericArabic;
61 | };
62 |
--------------------------------------------------------------------------------
/helpers/email.js:
--------------------------------------------------------------------------------
1 | const nodemailer = require('nodemailer');
2 |
3 | async function send(
4 | subject,
5 | text,
6 | to = process.env.MAILING_LIST,
7 | cb = () => {},
8 | ) {
9 | try {
10 | const transporter = nodemailer.createTransport({
11 | service: 'gmail',
12 | auth: {
13 | user: process.env.SENDER_EMAIL,
14 | pass: process.env.SENDER_PASSWORD,
15 | },
16 | });
17 | const message = {
18 | from: `<${process.env.SENDER_EMAIL}>`,
19 | to,
20 | subject,
21 | text: subject,
22 | html: text,
23 | };
24 | transporter.sendMail(message, (err, info) => {
25 | cb(err, info);
26 | });
27 | } catch (e) {
28 | // handle errors here
29 | }
30 | }
31 |
32 | module.exports = {
33 | send,
34 | };
35 |
--------------------------------------------------------------------------------
/helpers/emailTemplates.js:
--------------------------------------------------------------------------------
1 | function passwordResetTemplate(url) {
2 | return `
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | ژیر | گۆرینی تێپەرەوشە
11 |
12 |
13 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
107 | |
108 |
109 |
110 |
111 |
112 |
113 |
114 | گۆرینی تێپەرەوشە
115 | |
116 |
117 |
118 | |
119 |
120 |
121 |
122 |
123 |
124 |
125 | بۆ گۆرینی تێپەرەوشە کرتە لەو دوگمەیەی خوارەوە بکە ،گەر تۆ داواکاریەکەت نەناردوە دەتوانیت ئەم نامەیە بسریتەوە
126 | |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
140 | |
141 |
142 |
143 | |
144 |
145 |
146 |
147 | گەر کرتەکردن کارناکات دەتوانیت ئەم بەستەرەی خوارەوە کۆی بکەیت و بە وێبگەرەکەت راستەوخۆ پێیبگەیت
148 | ${url}
149 | |
150 |
151 |
152 |
153 | دەستەی برێوبەرایەتی ژیر
154 | |
155 |
156 |
157 | |
158 |
159 |
160 |
161 |
163 | |
164 |
165 |
166 |
167 |
168 | `;
169 | }
170 | function activateAccountTemplate(url) {
171 | return `
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 | ژیر | کاراکردنی هەژمار
180 |
181 |
182 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
276 | |
277 |
278 |
279 |
280 |
281 |
282 |
283 | کاراکردنی هەژمار
284 | |
285 |
286 |
287 | |
288 |
289 |
290 |
291 |
292 |
293 |
294 | بۆ کاراکردنی هەژمارەکەت کرتە لەم بەستەرەی خوارەوە بکە
295 | |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
309 | |
310 |
311 |
312 | |
313 |
314 |
315 |
316 | گەر کرتەکردن کارناکات دەتوانیت ئەم بەستەرەی خوارەوە کۆی بکەیت و بە وێبگەرەکەت راستەوخۆ پێیبگەیت
317 | ${url}
318 | |
319 |
320 |
321 |
322 | دەستەی برێوبەرایەتی ژیر
323 | |
324 |
325 |
326 | |
327 |
328 |
329 |
330 |
332 | |
333 |
334 |
335 |
336 |
337 | `;
338 | }
339 |
340 | module.exports = {
341 | passwordResetTemplate,
342 | activateAccountTemplate,
343 | };
344 |
--------------------------------------------------------------------------------
/helpers/isValidAPIKey.js:
--------------------------------------------------------------------------------
1 | const db = require('../database/connection');
2 |
3 | function isValidAPIKey(req, res, next) {
4 | const apiKey = req.headers['x-api-key'];
5 | if (!apiKey) {
6 | res.status(401).json({
7 | msg: 'API Key is missing please set x-api-key header ',
8 | });
9 | } else {
10 | db('user')
11 | .select()
12 | .where('api_key', apiKey)
13 | .where('active', 1)
14 | .where('deleted', 0)
15 | .limit(1)
16 | .then(([user]) => {
17 | const mutatedUser = user;
18 | if (user) {
19 | req.user = user;
20 | mutatedUser.from_api = 1;
21 | const isSandbox = req.headers['is-sandbox'];
22 | if (isSandbox === 1) mutatedUser.is_sandbox = 1;
23 | next();
24 | } else {
25 | res.status(401).json({
26 | msg:
27 | 'Invalid API key or the user is blocked from using the api contact support or call 0750 7665935',
28 | });
29 | }
30 | });
31 | }
32 | }
33 | module.exports = isValidAPIKey;
34 |
--------------------------------------------------------------------------------
/helpers/numberWithCommas.js:
--------------------------------------------------------------------------------
1 | module.exports = (x) => (`${x}`).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
2 |
--------------------------------------------------------------------------------
/helpers/options.js:
--------------------------------------------------------------------------------
1 | const options = {
2 | price_per_page: 200,
3 | };
4 |
5 | module.exports = options;
6 |
--------------------------------------------------------------------------------
/helpers/ratelimit.js:
--------------------------------------------------------------------------------
1 | const { RateLimiterMySQL } = require('rate-limiter-flexible');
2 | const db = require('../database/connection');
3 |
4 | const rateLimiter = new RateLimiterMySQL({
5 | blockDuration: 7200, // 2h
6 | storeClient: db,
7 | storeType: 'knex',
8 | keyPrefix: 'middleware',
9 | dbName: process.env.DB_NAME,
10 | tableName: 'rate_limit',
11 | points: 20, // 10 requests
12 | duration: 2, // per 1 second by IP
13 | });
14 |
15 | const rateLimiterMiddleware = (req, res, next) => {
16 | rateLimiter
17 | .consume(req.ip, 1)
18 | .then(() => {
19 | next();
20 | })
21 | .catch(() => {
22 | res.status(429).json({
23 | msg:
24 | 'بۆ ماوەی ٢ کاتژمێر بڵۆک کرایت بەهۆی ناردنی داواکاری زۆرەوە',
25 | });
26 | });
27 | };
28 | module.exports = rateLimiterMiddleware;
29 |
--------------------------------------------------------------------------------
/helpers/superagent.js:
--------------------------------------------------------------------------------
1 | const superagent = require('superagent');
2 |
3 | function constructHtmlError(err) {
4 | const { response } = err;
5 | if (response) {
6 | const { status, body } = response;
7 | let lis = '';
8 | if (status === 422) {
9 | const { errors } = body;
10 | (errors || []).forEach((e) => {
11 | lis += ` ${e.param} : ${e.msg} `;
12 | });
13 | } else if (status === 400 || status === 401 || status === 429) {
14 | lis += ` ${body.msg}`;
15 | } else if (status === 500) {
16 | lis += ` ${body.msg}`;
17 | } else {
18 | lis += ' ببورە هەڵەیەک لە راژە رویدا ';
19 | }
20 | const html = `
21 |
24 | `;
25 | return html;
26 | }
27 | return '';
28 | }
29 | const agent = superagent.agent();
30 | agent.on('error', (err) => {
31 | // eslint-disable-next-line no-param-reassign
32 | err.htmlError = constructHtmlError(err);
33 | return err;
34 | });
35 | export default agent;
36 |
--------------------------------------------------------------------------------
/middlewares/jwt/user.js:
--------------------------------------------------------------------------------
1 | const jwt = require('jsonwebtoken');
2 | const db = require('../../database/connection');
3 |
4 | module.exports = async (req, res, next) => {
5 | try {
6 | const { authorization } = req.headers;
7 | const parts = (authorization) ? authorization.split(' ') : [];
8 | if (parts[1]) {
9 | try {
10 | const decoded = jwt.verify(parts[1], process.env.USER_JWT_SECRET);
11 | // req.customer = decoded;
12 | req.user = {
13 | ...decoded.data,
14 | token: parts[1],
15 | };
16 | const [user] = await db('user').select()
17 | .where('active', 1)
18 | .where('deleted', 0)
19 | .andWhere('id', req.user.id)
20 | .limit(1);
21 | if (user) {
22 | next();
23 | } else {
24 | res.status(401).json({
25 | msg: 'Your account is not active, contact customer support ',
26 | });
27 | }
28 | } catch (err) {
29 | res.status(401).json({
30 | msg: err.toString(),
31 | });
32 | }
33 | } else {
34 | res.status(401).json({
35 | msg: 'invalid token provided',
36 | });
37 | }
38 | } catch (err) {
39 | res.status(401).json({
40 | msg: err.toString(),
41 | });
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/middlewares/validators/auth.js:
--------------------------------------------------------------------------------
1 | const { body, query } = require('express-validator');
2 | const validate = require('./validate');
3 | const { emailExisitsValidator } = require('./data/user');
4 | const blockedEmails = require('./blockedMailProviders');
5 |
6 | function blockedEmailValidator(v, bEmails = blockedEmails) {
7 | let passed = true;
8 | // eslint-disable-next-line no-restricted-syntax
9 | for (const blockedEmail of bEmails) {
10 | if (`${v}`.includes(blockedEmail)) {
11 | passed = false;
12 | break;
13 | }
14 | }
15 | if (passed) return Promise.resolve(true);
16 | return Promise.reject(new Error('ئەم جۆرە ئیمەیڵە بلۆک کراوە'));
17 | }
18 | module.exports = {
19 | emailPassValidator: [
20 | body('email')
21 | .exists()
22 | .withMessage('key does not exist')
23 | .trim()
24 | .custom((v) => blockedEmailValidator(v, blockedEmails)),
25 | body('password').exists().withMessage('key does not exist'),
26 | validate,
27 | ],
28 | passwordResetValidator: [
29 | body('token')
30 | .exists()
31 | .isHash('sha1')
32 | .withMessage('تۆکنی گۆرینی تێپەرەوشە هەڵەیە'),
33 | body('password_retype').optional().isString(),
34 | body('password')
35 | .optional()
36 | .isString()
37 |
38 | .withMessage('input is not valid string')
39 | .isLength({ min: 3 })
40 | .withMessage(' minimum length for user password is 2 chars')
41 | .custom((value, { req }) => {
42 | if (value === req.body.password_retype && value !== '') {
43 | return Promise.resolve('success');
44 | }
45 | return Promise.reject(
46 | new Error(
47 | 'user passwords dont match or both fields are empty',
48 | ),
49 | );
50 | })
51 | .withMessage('passwords dont match or bothe fields are empty'),
52 | validate,
53 | ],
54 | activateAccountValidator: [
55 | query('token')
56 | .exists()
57 | .isHash('sha1')
58 | .withMessage('تۆکنی کاراکردنی هەژمار هەڵەیە'),
59 | validate,
60 | ],
61 | phoneNoValidator: [
62 | body('phone_no')
63 | .exists()
64 | .matches(/^964[0-9][1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/i)
65 | .withMessage(
66 | 'phone number should be an iraqi phone and in this format 964 *** *******',
67 | ),
68 | validate,
69 | ],
70 | phoneNoWithCodeValidator: [
71 | body('phone_no')
72 | .exists()
73 | .matches(/^964[0-9][1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/i)
74 | .withMessage(
75 | 'phone number should be an iraqi phone and in this format 964 *** *******',
76 | ),
77 | body('code')
78 | .exists()
79 | .isInt({ min: 6 })
80 | .withMessage('invalid verification code'),
81 | validate,
82 | ],
83 | requestPasswordResetValidator: [
84 | body('email')
85 | .exists()
86 | .withMessage('بوونی نیە')
87 | .trim()
88 | .isString()
89 | .withMessage('input is not a valid string')
90 | .custom((v) => emailExisitsValidator(v))
91 | .custom((v) => blockedEmailValidator(v, blockedEmails)),
92 | validate,
93 | ],
94 | };
95 |
--------------------------------------------------------------------------------
/middlewares/validators/blockedMailProviders.js:
--------------------------------------------------------------------------------
1 | const blockedEmailProviders = [
2 | 'aramidth.com',
3 | '990ys.com',
4 | 'macosnine.com',
5 | 'timothyjsilverman.com',
6 | 'geeky83.com',
7 | 'upived.online',
8 | '250hz.com',
9 | 'edultry.com',
10 | 'lidte.com',
11 | 'mohmal.in',
12 | '559ai.com',
13 | 'fironia.com',
14 | 'combcub.com',
15 | 'fironia.com',
16 | 'boldhut.com',
17 | 'laklica.com',
18 | 'loopsnow.com',
19 | 'firemailbox.club',
20 | 'eamarian.com',
21 | 'mailna.co',
22 | 'jmail.com',
23 | 'dashseat.com',
24 | 'febula.com',
25 | 'izzum.com',
26 | 'is35.com',
27 | 'fmail.com',
28 | 'zher.com',
29 | 'vss6.com',
30 | 'majorsww.com',
31 | 'vipwxb.com',
32 | 'liaphoto.com',
33 | 'orxy.tech',
34 | 'burpcollaborator.net',
35 | '28woman.com',
36 | 'girtipo.com',
37 | 'cuoly.com',
38 | 'gail.com',
39 | 'z-er.com',
40 | 'cocyo.com',
41 | 'chomagor.com',
42 | 'tobuso.com',
43 | 'aomrock.com',
44 | 'nowdigit.com',
45 | 'hebgsw.com',
46 | 'firemailbox.club',
47 | 'wii999.com',
48 | 'otozuz.com',
49 | '0815.ru',
50 | '0wnd.net',
51 | '0wnd.org',
52 | '10minutemail.co.za',
53 | '10minutemail.com',
54 | '123-m.com',
55 | '1fsdfdsfsdf.tk',
56 | '1pad.de',
57 | '20minutemail.com',
58 | '21cn.com',
59 | '2fdgdfgdfgdf.tk',
60 | '2prong.com',
61 | '30minutemail.com',
62 | '33mail.com',
63 | '3trtretgfrfe.tk',
64 | '4gfdsgfdgfd.tk',
65 | '4warding.com',
66 | '5ghgfhfghfgh.tk',
67 | '6hjgjhgkilkj.tk',
68 | '6paq.com',
69 | '7tags.com',
70 | '9ox.net',
71 | 'a-bc.net',
72 | 'agedmail.com',
73 | 'ama-trade.de',
74 | 'amilegit.com',
75 | 'amiri.net',
76 | 'amiriindustries.com',
77 | 'anonmails.de',
78 | 'anonymbox.com',
79 | 'antichef.com',
80 | 'antichef.net',
81 | 'antireg.ru',
82 | 'antispam.de',
83 | 'antispammail.de',
84 | 'armyspy.com',
85 | 'artman-conception.com',
86 | 'azmeil.tk',
87 | 'baxomale.ht.cx',
88 | 'beefmilk.com',
89 | 'bigstring.com',
90 | 'binkmail.com',
91 | 'bio-muesli.net',
92 | 'bobmail.info',
93 | 'bodhi.lawlita.com',
94 | 'bofthew.com',
95 | 'bootybay.de',
96 | 'boun.cr',
97 | 'bouncr.com',
98 | 'breakthru.com',
99 | 'brefmail.com',
100 | 'bsnow.net',
101 | 'bspamfree.org',
102 | 'bugmenot.com',
103 | 'bund.us',
104 | 'burstmail.info',
105 | 'buymoreplays.com',
106 | 'byom.de',
107 | 'c2.hu',
108 | 'card.zp.ua',
109 | 'casualdx.com',
110 | 'cek.pm',
111 | 'centermail.com',
112 | 'centermail.net',
113 | 'chammy.info',
114 | 'childsavetrust.org',
115 | 'chogmail.com',
116 | 'choicemail1.com',
117 | 'clixser.com',
118 | 'cmail.net',
119 | 'cmail.org',
120 | 'coldemail.info',
121 | 'cool.fr.nf',
122 | 'courriel.fr.nf',
123 | 'courrieltemporaire.com',
124 | 'crapmail.org',
125 | 'cust.in',
126 | 'cuvox.de',
127 | 'd3p.dk',
128 | 'dacoolest.com',
129 | 'dandikmail.com',
130 | 'dayrep.com',
131 | 'dcemail.com',
132 | 'deadaddress.com',
133 | 'deadspam.com',
134 | 'delikkt.de',
135 | 'despam.it',
136 | 'despammed.com',
137 | 'devnullmail.com',
138 | 'dfgh.net',
139 | 'digitalsanctuary.com',
140 | 'dingbone.com',
141 | 'disposableaddress.com',
142 | 'disposableemailaddresses.com',
143 | 'disposableinbox.com',
144 | 'dispose.it',
145 | 'dispostable.com',
146 | 'dodgeit.com',
147 | 'dodgit.com',
148 | 'donemail.ru',
149 | 'dontreg.com',
150 | 'dontsendmespam.de',
151 | 'drdrb.net',
152 | 'dump-email.info',
153 | 'dumpandjunk.com',
154 | 'dumpyemail.com',
155 | 'e-mail.com',
156 | 'e-mail.org',
157 | 'e4ward.com',
158 | 'easytrashmail.com',
159 | 'einmalmail.de',
160 | 'einrot.com',
161 | 'eintagsmail.de',
162 | 'emailgo.de',
163 | 'emailias.com',
164 | 'emaillime.com',
165 | 'emailsensei.com',
166 | 'emailtemporanea.com',
167 | 'emailtemporanea.net',
168 | 'emailtemporar.ro',
169 | 'emailtemporario.com.br',
170 | 'emailthe.net',
171 | 'emailtmp.com',
172 | 'emailwarden.com',
173 | 'emailx.at.hm',
174 | 'emailxfer.com',
175 | 'emeil.in',
176 | 'emeil.ir',
177 | 'emz.net',
178 | 'ero-tube.org',
179 | 'evopo.com',
180 | 'explodemail.com',
181 | 'express.net.ua',
182 | 'eyepaste.com',
183 | 'fakeinbox.com',
184 | 'fakeinformation.com',
185 | 'fansworldwide.de',
186 | 'fantasymail.de',
187 | 'fightallspam.com',
188 | 'filzmail.com',
189 | 'fivemail.de',
190 | 'fleckens.hu',
191 | 'frapmail.com',
192 | 'friendlymail.co.uk',
193 | 'fuckingduh.com',
194 | 'fudgerub.com',
195 | 'fyii.de',
196 | 'garliclife.com',
197 | 'gehensiemirnichtaufdensack.de',
198 | 'get2mail.fr',
199 | 'getairmail.com',
200 | 'getmails.eu',
201 | 'getonemail.com',
202 | 'giantmail.de',
203 | 'girlsundertheinfluence.com',
204 | 'gishpuppy.com',
205 | 'gmial.com',
206 | 'goemailgo.com',
207 | 'gotmail.net',
208 | 'gotmail.org',
209 | 'gotti.otherinbox.com',
210 | 'great-host.in',
211 | 'greensloth.com',
212 | 'grr.la',
213 | 'gsrv.co.uk',
214 | 'guerillamail.biz',
215 | 'guerillamail.com',
216 | 'guerrillamail.biz',
217 | 'guerrillamail.com',
218 | 'guerrillamail.de',
219 | 'guerrillamail.info',
220 | 'guerrillamail.net',
221 | 'guerrillamail.org',
222 | 'guerrillamailblock.com',
223 | 'gustr.com',
224 | 'harakirimail.com',
225 | 'hat-geld.de',
226 | 'hatespam.org',
227 | 'herp.in',
228 | 'hidemail.de',
229 | 'hidzz.com',
230 | 'hmamail.com',
231 | 'hopemail.biz',
232 | 'ieh-mail.de',
233 | 'ikbenspamvrij.nl',
234 | 'imails.info',
235 | 'inbax.tk',
236 | 'inbox.si',
237 | 'inboxalias.com',
238 | 'inboxclean.com',
239 | 'inboxclean.org',
240 | 'infocom.zp.ua',
241 | 'instant-mail.de',
242 | 'ip6.li',
243 | 'irish2me.com',
244 | 'iwi.net',
245 | 'jetable.com',
246 | 'jetable.fr.nf',
247 | 'jetable.net',
248 | 'jetable.org',
249 | 'jnxjn.com',
250 | 'jourrapide.com',
251 | 'jsrsolutions.com',
252 | 'kasmail.com',
253 | 'kaspop.com',
254 | 'killmail.com',
255 | 'killmail.net',
256 | 'klassmaster.com',
257 | 'klzlk.com',
258 | 'koszmail.pl',
259 | 'kurzepost.de',
260 | 'lawlita.com',
261 | 'letthemeatspam.com',
262 | 'lhsdv.com',
263 | 'lifebyfood.com',
264 | 'link2mail.net',
265 | 'litedrop.com',
266 | 'lol.ovpn.to',
267 | 'lolfreak.net',
268 | 'lookugly.com',
269 | 'lortemail.dk',
270 | 'lr78.com',
271 | 'lroid.com',
272 | 'lukop.dk',
273 | 'm21.cc',
274 | 'mail-filter.com',
275 | 'mail-temporaire.fr',
276 | 'mail.by',
277 | 'mail.mezimages.net',
278 | 'mail.zp.ua',
279 | 'mail1a.de',
280 | 'mail21.cc',
281 | 'mail2rss.org',
282 | 'mail333.com',
283 | 'mailbidon.com',
284 | 'mailbiz.biz',
285 | 'mailblocks.com',
286 | 'mailbucket.org',
287 | 'mailcat.biz',
288 | 'mailcatch.com',
289 | 'mailde.de',
290 | 'mailde.info',
291 | 'maildrop.cc',
292 | 'maileimer.de',
293 | 'mailexpire.com',
294 | 'mailfa.tk',
295 | 'mailforspam.com',
296 | 'mailfreeonline.com',
297 | 'mailguard.me',
298 | 'mailin8r.com',
299 | 'mailinater.com',
300 | 'mailinator.com',
301 | 'mailinator.net',
302 | 'mailinator.org',
303 | 'mailinator2.com',
304 | 'mailincubator.com',
305 | 'mailismagic.com',
306 | 'mailme.lv',
307 | 'mailme24.com',
308 | 'mailmetrash.com',
309 | 'mailmoat.com',
310 | 'mailms.com',
311 | 'mailnesia.com',
312 | 'mailnull.com',
313 | 'mailorg.org',
314 | 'mailpick.biz',
315 | 'mailrock.biz',
316 | 'mailscrap.com',
317 | 'mailshell.com',
318 | 'mailsiphon.com',
319 | 'mailtemp.info',
320 | 'mailtome.de',
321 | 'mailtothis.com',
322 | 'mailtrash.net',
323 | 'mailtv.net',
324 | 'mailtv.tv',
325 | 'mailzilla.com',
326 | 'makemetheking.com',
327 | 'manybrain.com',
328 | 'mbx.cc',
329 | 'mega.zik.dj',
330 | 'meinspamschutz.de',
331 | 'meltmail.com',
332 | 'messagebeamer.de',
333 | 'mezimages.net',
334 | 'ministry-of-silly-walks.de',
335 | 'mintemail.com',
336 | 'misterpinball.de',
337 | 'moncourrier.fr.nf',
338 | 'monemail.fr.nf',
339 | 'monmail.fr.nf',
340 | 'monumentmail.com',
341 | 'mt2009.com',
342 | 'mt2014.com',
343 | 'mycard.net.ua',
344 | 'mycleaninbox.net',
345 | 'mymail-in.net',
346 | 'mypacks.net',
347 | 'mypartyclip.de',
348 | 'myphantomemail.com',
349 | 'mysamp.de',
350 | 'mytempemail.com',
351 | 'mytempmail.com',
352 | 'mytrashmail.com',
353 | 'nabuma.com',
354 | 'neomailbox.com',
355 | 'nepwk.com',
356 | 'nervmich.net',
357 | 'nervtmich.net',
358 | 'netmails.com',
359 | 'netmails.net',
360 | 'neverbox.com',
361 | 'nice-4u.com',
362 | 'nincsmail.hu',
363 | 'nnh.com',
364 | 'no-spam.ws',
365 | 'noblepioneer.com',
366 | 'nomail.pw',
367 | 'nomail.xl.cx',
368 | 'nomail2me.com',
369 | 'nomorespamemails.com',
370 | 'nospam.ze.tc',
371 | 'nospam4.us',
372 | 'nospamfor.us',
373 | 'nospammail.net',
374 | 'notmailinator.com',
375 | 'nowhere.org',
376 | 'nowmymail.com',
377 | 'nurfuerspam.de',
378 | 'nus.edu.sg',
379 | 'objectmail.com',
380 | 'obobbo.com',
381 | 'odnorazovoe.ru',
382 | 'oneoffemail.com',
383 | 'onewaymail.com',
384 | 'onlatedotcom.info',
385 | 'online.ms',
386 | 'opayq.com',
387 | 'ordinaryamerican.net',
388 | 'otherinbox.com',
389 | 'ovpn.to',
390 | 'owlpic.com',
391 | 'pancakemail.com',
392 | 'pcusers.otherinbox.com',
393 | 'pjjkp.com',
394 | 'plexolan.de',
395 | 'poczta.onet.pl',
396 | 'politikerclub.de',
397 | 'poofy.org',
398 | 'pookmail.com',
399 | 'privacy.net',
400 | 'privatdemail.net',
401 | 'proxymail.eu',
402 | 'prtnx.com',
403 | 'putthisinyourspamdatabase.com',
404 | 'putthisinyourspamdatabase.com',
405 | 'quickinbox.com',
406 | 'rcpt.at',
407 | 'reallymymail.com',
408 | 'realtyalerts.ca',
409 | 'recode.me',
410 | 'recursor.net',
411 | 'reliable-mail.com',
412 | 'rhyta.com',
413 | 'rmqkr.net',
414 | 'royal.net',
415 | 'rtrtr.com',
416 | 's0ny.net',
417 | 'safe-mail.net',
418 | 'safersignup.de',
419 | 'safetymail.info',
420 | 'safetypost.de',
421 | 'saynotospams.com',
422 | 'schafmail.de',
423 | 'schrott-email.de',
424 | 'secretemail.de',
425 | 'secure-mail.biz',
426 | 'senseless-entertainment.com',
427 | 'services391.com',
428 | 'sharklasers.com',
429 | 'shieldemail.com',
430 | 'shiftmail.com',
431 | 'shitmail.me',
432 | 'shitware.nl',
433 | 'shmeriously.com',
434 | 'shortmail.net',
435 | 'sibmail.com',
436 | 'sinnlos-mail.de',
437 | 'slapsfromlastnight.com',
438 | 'slaskpost.se',
439 | 'smashmail.de',
440 | 'smellfear.com',
441 | 'snakemail.com',
442 | 'sneakemail.com',
443 | 'sneakmail.de',
444 | 'snkmail.com',
445 | 'sofimail.com',
446 | 'solvemail.info',
447 | 'sogetthis.com',
448 | 'soodonims.com',
449 | 'spam4.me',
450 | 'spamail.de',
451 | 'spamarrest.com',
452 | 'spambob.net',
453 | 'spambog.ru',
454 | 'spambox.us',
455 | 'spamcannon.com',
456 | 'spamcannon.net',
457 | 'spamcon.org',
458 | 'spamcorptastic.com',
459 | 'spamcowboy.com',
460 | 'spamcowboy.net',
461 | 'spamcowboy.org',
462 | 'spamday.com',
463 | 'spamex.com',
464 | 'spamfree.eu',
465 | 'spamfree24.com',
466 | 'spamfree24.de',
467 | 'spamfree24.org',
468 | 'spamgoes.in',
469 | 'spamgourmet.com',
470 | 'spamgourmet.net',
471 | 'spamgourmet.org',
472 | 'spamherelots.com',
473 | 'spamherelots.com',
474 | 'spamhereplease.com',
475 | 'spamhereplease.com',
476 | 'spamhole.com',
477 | 'spamify.com',
478 | 'spaml.de',
479 | 'spammotel.com',
480 | 'spamobox.com',
481 | 'spamslicer.com',
482 | 'spamspot.com',
483 | 'spamthis.co.uk',
484 | 'spamtroll.net',
485 | 'speed.1s.fr',
486 | 'spoofmail.de',
487 | 'stuffmail.de',
488 | 'super-auswahl.de',
489 | 'supergreatmail.com',
490 | 'supermailer.jp',
491 | 'superrito.com',
492 | 'superstachel.de',
493 | 'suremail.info',
494 | 'talkinator.com',
495 | 'teewars.org',
496 | 'teleworm.com',
497 | 'teleworm.us',
498 | 'temp-mail.org',
499 | 'temp-mail.ru',
500 | 'tempe-mail.com',
501 | 'tempemail.co.za',
502 | 'tempemail.com',
503 | 'tempemail.net',
504 | 'tempemail.net',
505 | 'tempinbox.co.uk',
506 | 'tempinbox.com',
507 | 'tempmail.eu',
508 | 'tempmaildemo.com',
509 | 'tempmailer.com',
510 | 'tempmailer.de',
511 | 'tempomail.fr',
512 | 'temporaryemail.net',
513 | 'temporaryforwarding.com',
514 | 'temporaryinbox.com',
515 | 'temporarymailaddress.com',
516 | 'tempthe.net',
517 | 'thankyou2010.com',
518 | 'thc.st',
519 | 'thelimestones.com',
520 | 'thisisnotmyrealemail.com',
521 | 'thismail.net',
522 | 'throwawayemailaddress.com',
523 | 'tilien.com',
524 | 'tittbit.in',
525 | 'tizi.com',
526 | 'tmailinator.com',
527 | 'toomail.biz',
528 | 'topranklist.de',
529 | 'tradermail.info',
530 | 'trash-mail.at',
531 | 'trash-mail.com',
532 | 'trash-mail.de',
533 | 'trash2009.com',
534 | 'trashdevil.com',
535 | 'trashemail.de',
536 | 'trashmail.at',
537 | 'trashmail.com',
538 | 'trashmail.de',
539 | 'trashmail.me',
540 | 'trashmail.net',
541 | 'trashmail.org',
542 | 'trashymail.com',
543 | 'trialmail.de',
544 | 'trillianpro.com',
545 | 'twinmail.de',
546 | 'tyldd.com',
547 | 'uggsrock.com',
548 | 'umail.net',
549 | 'uroid.com',
550 | 'us.af',
551 | 'venompen.com',
552 | 'veryrealemail.com',
553 | 'viditag.com',
554 | 'viralplays.com',
555 | 'vpn.st',
556 | 'vsimcard.com',
557 | 'vubby.com',
558 | 'wasteland.rfc822.org',
559 | 'webemail.me',
560 | 'weg-werf-email.de',
561 | 'wegwerf-emails.de',
562 | 'wegwerfadresse.de',
563 | 'wegwerfemail.com',
564 | 'wegwerfemail.de',
565 | 'wegwerfmail.de',
566 | 'wegwerfmail.info',
567 | 'wegwerfmail.net',
568 | 'wegwerfmail.org',
569 | 'wh4f.org',
570 | 'whyspam.me',
571 | 'willhackforfood.biz',
572 | 'willselfdestruct.com',
573 | 'winemaven.info',
574 | 'wronghead.com',
575 | 'www.e4ward.com',
576 | 'www.mailinator.com',
577 | 'wwwnew.eu',
578 | 'x.ip6.li',
579 | 'xagloo.com',
580 | 'xemaps.com',
581 | 'xents.com',
582 | 'xmaily.com',
583 | 'yep.it',
584 | 'yogamaven.com',
585 | 'yopmail.com',
586 | 'yopmail.fr',
587 | 'yopmail.net',
588 | 'yourdomain.com',
589 | 'yuurok.com',
590 | 'z1p.biz',
591 | 'za.com',
592 | 'zehnminuten.de',
593 | 'zehnminutenmail.de',
594 | 'zippymail.info',
595 | 'zoemail.net',
596 | 'zomg.info',
597 | '126.com',
598 | '163.com',
599 | 'yeah.net',
600 | '139.com',
601 | 'mailed.ro',
602 | 'maildx.com',
603 | 'yopmail.pp.ua',
604 | 'mozej.com',
605 | 'pay-mon.com',
606 | 'zep-hyr.com',
607 | 'travala10.com',
608 | 'zippiex.com',
609 | 'poly-swarm.com',
610 | '1shivom.com',
611 | 'fidelium10.com',
612 | 'hubii-network.com',
613 | 'hurify1.com',
614 | 'bit-degree.com',
615 | 'Hatmail.ir',
616 | 'Gmeil.site',
617 | 'Outlok.site',
618 | 'sika3.com',
619 | 'tapi.re',
620 | 'kagi.be',
621 | 'nagi.be',
622 | 'heisei.be',
623 | 'honeys.be',
624 | 'mbox.re',
625 | 'kbox.li',
626 | 'ponp.be',
627 | 'risu.be',
628 | 'fuwa.be',
629 | 'usako.net',
630 | 'eay.jp',
631 | 'via.tokyo.jp',
632 | 'ichigo.me',
633 | 'choco.la',
634 | 'cream.pink',
635 | 'merry.pink',
636 | 'neko2.net',
637 | 'fuwamofu.com',
638 | 'ruru.be',
639 | 'macr2.com',
640 | 'f5.si',
641 | 'ahk.jp',
642 | 'svk.jp',
643 | 'moimoi.re',
644 | 'kksm.be',
645 | 'tensi.org',
646 | 'test.com',
647 | 'niekie.com',
648 | 'd3ff.com',
649 | 'datakop.com',
650 | ];
651 |
652 | module.exports = blockedEmailProviders;
653 |
--------------------------------------------------------------------------------
/middlewares/validators/common/dataGrid.js:
--------------------------------------------------------------------------------
1 | const { query } = require('express-validator');
2 | const validate = require('../validate');
3 |
4 | module.exports = [
5 | query('pageSize')
6 | .customSanitizer((value) => ((value) || 10))
7 | .isInt({ gt: 0 })
8 | .withMessage('input should be a valid integer'),
9 | query('page')
10 | .customSanitizer((value) => ((value) || 0))
11 | .isInt({ gt: -1 }).withMessage('input should be a valid integer'),
12 | query('sorted')
13 | .customSanitizer((value) => ((value) || []))
14 | .isArray()
15 | .withMessage('input must be an array')
16 | .custom((value) => {
17 | if (value.length > 5) { return Promise.reject(new Error('array must contain less than 5 items ')); }
18 | return Promise.resolve(true);
19 | }),
20 | query('sorted.*')
21 | .isString()
22 | .matches(/^([a-zA-Z0-9._ . _])+:([a-zA-Z0-9 % \- ء-ي قوەرتیئحۆپڕاسدفگهژکلزخجڤبێنمضچى . _ - ـ = + * & $ %])+$/i)
23 | .withMessage('invalid format should be columnName:value')
24 | .customSanitizer((value) => {
25 | const splitedValue = value.split(':');
26 | return {
27 | column: splitedValue[0],
28 | value: splitedValue[1],
29 | };
30 | }),
31 | query('sorted.*.column')
32 | .exists(),
33 | query('sorted.*.value')
34 | .isIn(['asc', 'desc'])
35 | .withMessage('allowed values for sorted column value is `asc` or `desc`'),
36 | query('filtered')
37 | .customSanitizer((value) => ((value) || []))
38 | .isArray()
39 | .withMessage('input must be an array')
40 | .custom((value) => {
41 | if (value.length > 11) { return Promise.reject(new Error('array must contain less than 10 items ')); }
42 | return Promise.resolve(true);
43 | }),
44 | query('filtered.*')
45 | .isString()
46 | // eslint-disable-next-line no-useless-escape
47 | .matches(/^([a-zA-Z0-9._])+:([a-zA-Z0-9 % \- ء-ي قوەرتیئحۆپڕاسدفگهژکلزخجڤبێنمضچى . - ـ = + * & $ %])+$/i)
48 | .withMessage('invalid format should be columnName:value')
49 | .customSanitizer((value) => {
50 | const splitedValue = value.split(':');
51 | return {
52 | column: splitedValue[0],
53 | value: splitedValue[1],
54 | };
55 | }),
56 | query('filtered.*.column')
57 | .matches(/^([a-zA-Z0-9. _ـ - #])+$/i)
58 | .withMessage('column name is not alphanumeric'),
59 | query('filtered.*.value')
60 | .escape(),
61 | validate,
62 | ];
63 |
--------------------------------------------------------------------------------
/middlewares/validators/common/lazyLoad.js:
--------------------------------------------------------------------------------
1 | const { query } = require('express-validator');
2 | const validate = require('../validate');
3 |
4 | module.exports = [
5 | query('limit')
6 | .exists()
7 | .withMessage('limit value is required')
8 | .isInt({ gt: 0 })
9 | .withMessage('invalid limit value')
10 | .customSanitizer((value) => {
11 | if (Number.isNaN(value)) {
12 | return 10;
13 | }
14 | return Number.parseInt(value, 10);
15 | }),
16 | query('offset')
17 | .exists()
18 | .withMessage('offset value is required')
19 | .isInt({ gt: -1 })
20 | .withMessage('invalid offset value')
21 | .customSanitizer((value) => {
22 | if (Number.isNaN(value)) {
23 | return 0;
24 | }
25 | return Number.parseInt(value, 10);
26 | }),
27 | validate,
28 | ];
29 |
--------------------------------------------------------------------------------
/middlewares/validators/common/searchQuery.js:
--------------------------------------------------------------------------------
1 | const { query } = require('express-validator');
2 | const validate = require('../validate');
3 |
4 | module.exports = [
5 | query('q')
6 | .optional()
7 | .isLength({ min: 1, max: 255 })
8 | .withMessage('invalid query length must be between 1->255'),
9 | query('city_id')
10 | .optional()
11 | .isInt({ gt: -1 })
12 | .withMessage('invalid city_id must ba a valid integer'),
13 | validate,
14 | ];
15 |
--------------------------------------------------------------------------------
/middlewares/validators/common/storage.js:
--------------------------------------------------------------------------------
1 | const { body } = require('express-validator');
2 | const validate = require('../validate');
3 |
4 | const maxFileSize = 4e+6;
5 | module.exports = {
6 | putObjectsValidator: [
7 | body('files')
8 | .exists().withMessage('key does not exist')
9 | .isArray({ min: 1 })
10 | .withMessage('input should be an array with at least one elemnt'),
11 | body('files.*.name')
12 | .exists()
13 | .trim()
14 | .isString(),
15 | body('files.*.type')
16 | .exists()
17 | .trim()
18 | .isString()
19 | .isIn([
20 | 'image/png',
21 | 'image/jpeg',
22 | 'image/pjpeg',
23 | 'image/gif',
24 | 'application/x-compressed',
25 | 'application/x-zip-compressed',
26 | 'application/zip',
27 | 'multipart/x-zip',
28 | 'audio/mpeg3',
29 | 'audio/x-mpeg-3',
30 | 'video/mpeg',
31 | 'video/x-mpeg',
32 | 'audio/mpeg',
33 | 'application/pdf',
34 | 'application/msword',
35 | 'application/excel',
36 | ])
37 | .withMessage('file type not allowed'),
38 | body('files.*.base64')
39 | .exists()
40 | .trim()
41 | .isString()
42 | .isLength({ max: maxFileSize })
43 | .withMessage('maximum file size excceded 6mb'),
44 | validate,
45 | ],
46 | deleteObjectsValidator: [
47 | body('files')
48 | .exists().withMessage('key does not exist')
49 | .isArray({ min: 1 })
50 | .withMessage('input should be an array with at least one elemnt'),
51 | body('files.*')
52 | .exists()
53 | .trim()
54 | .isString(),
55 | validate,
56 | ],
57 |
58 | };
59 |
--------------------------------------------------------------------------------
/middlewares/validators/data/idPay.js:
--------------------------------------------------------------------------------
1 | const db = require('../../../database/connection');
2 |
3 | async function checkDuplicateTransactionId(
4 | transactionId = 0,
5 | paymentTransactionId = 0,
6 | ) {
7 | const [transaction] = await db('user_transaction')
8 | .count('id as count')
9 | .where('id', transactionId)
10 | .andWhere('transaction_id', paymentTransactionId)
11 | .andWhere('confirmed', 1);
12 | if (transaction && transaction.count > 1) {
13 | return Promise.reject(
14 | new Error('ناتوانیت هەمان ID بەکارببەیت بۆ دووبارە پارەدانەوە'),
15 | );
16 | }
17 | return Promise.resolve(true);
18 | }
19 |
20 | module.exports = {
21 | checkDuplicateTransactionId,
22 | };
23 |
--------------------------------------------------------------------------------
/middlewares/validators/data/job.js:
--------------------------------------------------------------------------------
1 | const db = require('../../../database/connection');
2 |
3 | async function balanceValidator(userId = 0, files = []) {
4 | const [pages] = await db('user_transaction')
5 | .select(db.raw('SUM(COALESCE(page_count, 0)) as totalPages'))
6 | .andWhere('user_id', userId)
7 | .andWhere('confirmed', 1);
8 | if (pages && pages.totalPages > 0 && pages.totalPages >= files.length) {
9 | return Promise.resolve(true);
10 | }
11 | return Promise.reject(
12 | new Error(
13 | 'تکایە باڵانس پربکەرەوە باڵانسی ماوە کەمترە لە ژمارەی فایلەکان',
14 | ),
15 | );
16 | }
17 | async function checkUserActiveJobs(userId = 0) {
18 | const [jobs] = await db('job')
19 | .count('id as count')
20 | .whereIn('status', ['pending', 'queued', 'processing'])
21 | .andWhere('user_id', userId);
22 | if (jobs && jobs.count < 3) {
23 | return Promise.resolve(true);
24 | }
25 | return Promise.reject(
26 | new Error('تکایە چاوەروان بە تاوەکو ٣ کردارەکەی دیکەت تەواو دەبن'),
27 | );
28 | }
29 | async function rateJobValidator(userId = 0, jobId = 0) {
30 | const [job] = await db('job')
31 | .select()
32 | .andWhere('user_id', userId)
33 | .andWhere('id', jobId)
34 | .limit(1);
35 | if (job) {
36 | if (job.status === 'completed') {
37 | if (job.rate > 0) {
38 | return Promise.reject(
39 | new Error(
40 | 'ئەم سکانە هەڵسەنگانی بۆ کراوە ناتوانیت دوبارە کردارەکە ئەنجام بدەیتەوە',
41 | ),
42 | );
43 | }
44 | } else {
45 | return Promise.reject(
46 | new Error(
47 | 'ناتوانیت هەڵسەنگاندن بۆ کردارێک بکەیت کە تەواو نەبووە',
48 | ),
49 | );
50 | }
51 | return Promise.resolve(true);
52 | }
53 | return Promise.reject(
54 | new Error('تۆ ئەم سکانەت دروست نەکردوە یان هەڵەیەک رویدا'),
55 | );
56 | }
57 |
58 | module.exports = {
59 | rateJobValidator,
60 | checkUserActiveJobs,
61 | balanceValidator,
62 | };
63 |
--------------------------------------------------------------------------------
/middlewares/validators/data/user.js:
--------------------------------------------------------------------------------
1 | const db = require('../../../database/connection');
2 |
3 | async function duplicateEmailValidator(email) {
4 | return db('user')
5 | .count('id as count')
6 | .where('email', email)
7 | .then(([d]) => {
8 | if (d.count > 0) {
9 | return Promise.reject(new Error('پۆستی ئەلیکترۆنی بوونی هەیە'));
10 | }
11 | return Promise.resolve(true);
12 | });
13 | }
14 | async function duplicateEmailUserUpdateValidator(email, user) {
15 | return db('user')
16 | .count('id as count')
17 | .where('email', email)
18 | .andWhere('id', '<>', user.id)
19 | .then(([d]) => {
20 | if (d.count > 0) {
21 | return Promise.reject(
22 | new Error('پۆستی ئەلئکترۆنی بەکارهاتووە'),
23 | );
24 | }
25 | return Promise.resolve(true);
26 | });
27 | }
28 | async function duplicatePhoneNumberUserUpdateValidator(phone, user) {
29 | if (phone === '' || phone == null) {
30 | return Promise.resolve(true);
31 | }
32 | return db('user')
33 | .count('id as count')
34 | .where('phone_no', phone)
35 | .andWhere('id', '<>', user.id)
36 | .then(([d]) => {
37 | if (d.count > 0) {
38 | return Promise.reject(
39 | new Error(
40 | 'ژمارەی مۆبایل لە هەژمارێکی دیکە بەکارهاتووە تکایە پەیوەندیمان پێوەبکە',
41 | ),
42 | );
43 | }
44 | return Promise.resolve(true);
45 | });
46 | }
47 | async function emailExisitsValidator(email) {
48 | return db('user')
49 | .count('id as count')
50 | .where('email', email)
51 | .then(([d]) => {
52 | if (d.count > 0) {
53 | return Promise.resolve(true);
54 | }
55 | return Promise.reject(new Error('پۆستی ئەلیکترۆنی بوونی نیە'));
56 | });
57 | }
58 |
59 | module.exports = {
60 | duplicateEmailValidator,
61 | duplicateEmailUserUpdateValidator,
62 | duplicatePhoneNumberUserUpdateValidator,
63 | emailExisitsValidator,
64 | };
65 |
--------------------------------------------------------------------------------
/middlewares/validators/fastpay.js:
--------------------------------------------------------------------------------
1 | const { query } = require('express-validator');
2 |
3 | const validate = require('./validate');
4 |
5 | module.exports = {
6 | fastpayRechargeValidator: [
7 | query('amount').exists()
8 | .isIn(['8000', '30000', '50000', 5000, 30000, 50000])
9 | .withMessage('invalid purchase amount'),
10 | validate,
11 | ],
12 |
13 | };
14 |
--------------------------------------------------------------------------------
/middlewares/validators/idPay.js:
--------------------------------------------------------------------------------
1 | const { body, param } = require('express-validator');
2 |
3 | const { checkDuplicateTransactionId } = require('./data/idPay');
4 | const validate = require('./validate');
5 |
6 | module.exports = {
7 | payValidator: [
8 | param('amount')
9 | .exists()
10 | .isIn([400000, 2500000, 700000, 4000000])
11 | .withMessage('بڕی پارەی نێردراو هەڵەیە'),
12 |
13 | validate,
14 | ],
15 | verifyValidator: [
16 | body('id').exists(),
17 | body('order_id')
18 | .exists()
19 | .custom((v, { req }) => checkDuplicateTransactionId(v, req.body.id)),
20 | validate,
21 | ],
22 | };
23 |
--------------------------------------------------------------------------------
/middlewares/validators/job.js:
--------------------------------------------------------------------------------
1 | const { body, param } = require('express-validator');
2 |
3 | const prettyBytes = require('pretty-bytes');
4 | const validate = require('./validate');
5 |
6 | const {
7 | checkUserActiveJobs,
8 | balanceValidator,
9 | rateJobValidator: rateJobDataValidator,
10 | } = require('./data/job');
11 |
12 | const maxFileSize = 1.2e+7; // file limit is now 12mb
13 | const allowedImageTypes = [
14 | 'image/jpg',
15 | 'image/jpeg',
16 | 'image/jfif',
17 | 'image/png',
18 | 'image/webp',
19 | 'image/bmp',
20 | 'image/tiff',
21 | 'image/pjpeg',
22 | 'image/tif',
23 | 'image/png',
24 | ];
25 | module.exports = {
26 | createValidator: [
27 | body('job_id')
28 | .exists()
29 | .isUUID()
30 | .withMessage('ئایدی دروستکراو هەڵەیە')
31 | .custom((v, { req }) => checkUserActiveJobs(req.user.id)),
32 | body('group_name').exists().isString(),
33 | body('lang').exists().isString(),
34 | body('files')
35 | .exists()
36 | .withMessage('key does not exist')
37 | .isArray({ min: 1 })
38 | .withMessage('دەبێت بەلایەنی کەم فایلێک بنێریت')
39 | .isArray({ max: 100 })
40 | .withMessage('دەبێت کەمتر لە 100 فایل هەڵبژێریت بۆ هەر جارێک')
41 | .custom((v, { req }) => balanceValidator(req.user.id, v)),
42 | body('files.*.name').exists().trim().isString(),
43 | body('files.*.index').exists().isInt(),
44 | body('files.*.extention').exists().isString(),
45 | body('files.*.type')
46 | .exists()
47 | .trim()
48 | .isString()
49 | .isIn(allowedImageTypes)
50 | .withMessage('ئەم جۆرە فایلە رێگەپێدراو نیە'),
51 | body('files.*.base64')
52 | .exists()
53 | .trim()
54 | .isString()
55 | .isLength({ max: maxFileSize })
56 | .withMessage(
57 | `گەورەترین قەبارەی فایل بریتیە لە ${prettyBytes(maxFileSize)}`,
58 | ),
59 | validate,
60 | ],
61 | createExternalValidator: [
62 | body('job_id').exists().isUUID().withMessage('ئایدی دروستکراو هەڵەیە'),
63 | body('group_name').exists().isString(),
64 | body('lang').exists().isString(),
65 | body('files')
66 | .exists()
67 | .withMessage('key does not exist')
68 | .isArray({ min: 1 })
69 | .withMessage('دەبێت بەلایەنی کەم فایلێک بنێریت')
70 | .isArray({ max: 100 })
71 | .withMessage('دەبێت کەمتر لە 100 فایل هەڵبژێریت بۆ هەر جارێک')
72 | .custom((v, { req }) => balanceValidator(req.user.id, v)),
73 | body('files.*.name').exists().trim().isString(),
74 | body('files.*.index').exists().isInt(),
75 | body('files.*.extention').exists().isString(),
76 | body('files.*.type')
77 | .exists()
78 | .trim()
79 | .isString()
80 | .isIn(allowedImageTypes)
81 | .withMessage('ئەم جۆرە فایلە رێگەپێدراو نیە'),
82 | body('files.*.base64')
83 | .exists()
84 | .trim()
85 | .isString()
86 | .isLength({ max: maxFileSize })
87 | .withMessage(
88 | `گەورەترین قەبارەی فایل بریتیە لە ${prettyBytes(maxFileSize)}`,
89 | ),
90 | body('callback')
91 | .exists()
92 | .isString()
93 | .withMessage('callback must be a valid url'),
94 | validate,
95 | ],
96 | readSingleJobValidator: [param('job_id').exists().isUUID(), validate],
97 | rateJobValidator: [
98 | param('job_id')
99 | .exists()
100 | .isUUID()
101 | .custom((v, { req }) => rateJobDataValidator(req.user.id, v)),
102 | body('rate')
103 | .exists()
104 | .isIn([1, 2, 3, 4, 5])
105 | .withMessage(
106 | 'هەڵەیەک رویدا لەکاتی دەنگدان، ژمارەیەکی هەڵە نێردراوە',
107 | ),
108 | validate,
109 | ],
110 | };
111 |
--------------------------------------------------------------------------------
/middlewares/validators/user.js:
--------------------------------------------------------------------------------
1 | const { body, param } = require('express-validator');
2 |
3 | // const moment = require('moment');
4 | const validate = require('./validate');
5 | const blockedEmails = require('./blockedMailProviders');
6 |
7 | const {
8 | duplicateEmailValidator,
9 | duplicateEmailUserUpdateValidator,
10 | duplicatePhoneNumberUserUpdateValidator,
11 | } = require('./data/user');
12 |
13 | // moment.suppressDeprecationWarnings = true;
14 | function blockedEmailValidator(v, bEmails = blockedEmails) {
15 | let passed = true;
16 | // eslint-disable-next-line no-restricted-syntax
17 | for (const blockedEmail of bEmails) {
18 | if (`${v}`.includes(blockedEmail)) {
19 | passed = false;
20 | break;
21 | }
22 | }
23 | if (passed) return Promise.resolve(true);
24 | return Promise.reject(new Error('ئەم جۆرە ئیمەیڵە بلۆک کراوە'));
25 | }
26 | module.exports = {
27 | createValidator: [
28 | body('name')
29 | .exists()
30 | .withMessage('بوونی نیە')
31 | .trim()
32 | .isString()
33 | .withMessage('input is not a valid string'),
34 | body('company_name')
35 | .exists()
36 | .withMessage('بوونی نیە')
37 | .trim()
38 | .isString()
39 | .withMessage('input is not a valid string'),
40 | body('email')
41 | .exists()
42 | .withMessage('بوونی نیە')
43 | .trim()
44 | .isString()
45 | .withMessage('input is not a valid string')
46 | .custom((v) => duplicateEmailValidator(v))
47 | .custom((v) => blockedEmailValidator(v, blockedEmails)),
48 |
49 | body('password_retype').exists().withMessage('بوونی نیە'),
50 | body('password')
51 | .exists()
52 | .isString()
53 | .withMessage('input is not valid string')
54 | .isLength({ min: 3 })
55 | .withMessage('تێپەرەوشە دەبێت ٢ پیت بێت بەلایەنی کەم')
56 | .custom((value, { req }) => {
57 | if (value === req.body.password_retype && value !== '') {
58 | return Promise.resolve('success');
59 | }
60 | return Promise.reject(new Error('تێپەرەوشەکان جیاوازن'));
61 | }),
62 | validate,
63 | ],
64 | updateValidator: [
65 | // param('user_id')
66 | // .exists()
67 | // .withMessage('key does not exisit')
68 | // .isInt()
69 | // .withMessage('input must be a valid integer'),
70 | body('name')
71 | .exists()
72 | .withMessage('بوونی نیە')
73 | .trim()
74 | .isString()
75 | .withMessage('input is not a valid string')
76 | .isLength({ min: 2 })
77 | .withMessage('ناوی بەکارهێنەر دەبێت بەلایەنی کەمەوە ٢ پیت بێت'),
78 | // body('gender')
79 | // .exists()
80 | // .withMessage('بوونی نیە')
81 | // .isIn(['male', 'female', 'unspecified']),
82 | // body('birthdate')
83 | // .exists()
84 | // .withMessage('بوونی نیە')
85 | // .custom((value) => {
86 | // const date = moment(value);
87 | // if (date.isValid()) {
88 | // if (moment(value).format('YYYY-MM-DD') === value) {
89 | // return Promise.resolve(true);
90 | // }
91 | // return Promise.reject(new Error('invalid date format'));
92 | // }
93 | // return Promise.reject(new Error('invalid date'));
94 | // }),
95 | // body('active')
96 | // .exists()
97 | // .withMessage('بوونی نیە')
98 | // .isBoolean()
99 | // .withMessage('input should be a valid boolean'),
100 | body('email')
101 | .exists()
102 | .withMessage('بوونی نیە')
103 | .trim()
104 | .isString()
105 | .custom((v, { req }) => duplicateEmailUserUpdateValidator(v, req.user))
106 | .custom((v) => blockedEmailValidator(v, blockedEmails)),
107 | body('phone_no')
108 | .exists()
109 | .trim()
110 | .isString()
111 | .matches(
112 | /^\+964[0-9][1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/i,
113 | )
114 | .withMessage(
115 | `ژمارەی مۆبایل دەبێت لەم شێوەیە بێت بۆ نموونە
116 | 9647727903366+`,
117 | )
118 | .custom((v, { req }) => duplicatePhoneNumberUserUpdateValidator(v, req.user)),
119 | body('password_retype').optional().isString(),
120 | body('password')
121 | .optional()
122 | .isString()
123 | .isLength({ min: 3 })
124 | .withMessage('تێپەرەوشە دەبێت بەلایەنی کەم ٣ پیت بێت')
125 | .custom((value, { req }) => {
126 | if (value === req.body.password_retype && value !== '') {
127 | return Promise.resolve('success');
128 | }
129 | return Promise.reject(
130 | new Error(
131 | 'تێپەرەوشەکان لەیەک ناکەن یاخود هەردووکیان بەتاڵن',
132 | ),
133 | );
134 | })
135 | .withMessage('تێپەرەوشەکان لەیەک ناکەن یاخود هەردووکیان بەتاڵن'),
136 | validate,
137 | ],
138 | readSingleUserValidator: [
139 | param('user_id')
140 | .exists()
141 | .isInt()
142 | .withMessage('value should be valid integer'),
143 | validate,
144 | ],
145 | };
146 |
--------------------------------------------------------------------------------
/middlewares/validators/validate.js:
--------------------------------------------------------------------------------
1 | const { validationResult } = require('express-validator');
2 |
3 | module.exports = (req, res, next) => {
4 | const errors = validationResult(req);
5 | if (!errors.isEmpty()) {
6 | return res.status(422).json({ errors: errors.array() });
7 | }
8 | next();
9 | return null;
10 | };
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zhir-api",
3 | "version": "1.0.0",
4 | "description": "this acts as the fron rest api for zhir.io",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node ./bin/server.js",
8 | "dev": "npx nodemon ./bin/server.js",
9 | "db:migrate": "npx knex migrate:latest --knexfile ./database/knexfile.js",
10 | "db:seed": "npx knex seed:run --knexfile ./database/knexfile.js",
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/zhir-io/API.git"
16 | },
17 | "keywords": [
18 | "zhir.io",
19 | "zhir"
20 | ],
21 | "author": "aramrafeq2@gmail.com",
22 | "license": "ISC",
23 | "bugs": {
24 | "url": "https://github.com/zhir-io/API/issues"
25 | },
26 | "homepage": "https://github.com/zhir-io/API#readme",
27 | "devDependencies": {
28 | "eslint": "^7.32.0",
29 | "eslint-config-airbnb-base": "^14.2.1",
30 | "eslint-plugin-import": "^2.24.2",
31 | "nodemon": "^2.0.13"
32 | },
33 | "dependencies": {
34 | "aws-sdk": "^2.995.0",
35 | "chalk": "^4.1.2",
36 | "compression": "^1.7.4",
37 | "cors": "^2.8.5",
38 | "dayjs": "^1.10.7",
39 | "debug": "^4.3.2",
40 | "dotenv": "^10.0.0",
41 | "express": "^4.17.1",
42 | "express-validator": "^6.12.1",
43 | "helmet": "^4.6.0",
44 | "jsonwebtoken": "^8.5.1",
45 | "knex": "^0.95.11",
46 | "md5": "^2.3.0",
47 | "moment": "^2.29.1",
48 | "morgan": "^1.10.0",
49 | "mysql": "^2.18.1",
50 | "nodemailer": "^6.6.5",
51 | "pretty-bytes": "^5.6.0",
52 | "rate-limiter-flexible": "^2.2.4",
53 | "rotating-file-stream": "^2.1.5",
54 | "sha1": "^1.1.1",
55 | "superagent": "^6.1.0",
56 | "uniqid": "^5.4.0",
57 | "uuid": "^8.3.2"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/query/job.js:
--------------------------------------------------------------------------------
1 | const uniqid = require('uniqid');
2 | const dayjs = require('dayjs');
3 | const utc = require('dayjs/plugin/utc');
4 | const db = require('../database/connection');
5 | const uploader = require('../helpers/aws/putObjects');
6 | const options = require('../helpers/options');
7 |
8 | dayjs.extend(utc);
9 | const pricePerPage = options.price_per_page;
10 |
11 | async function createJobQuery(body, user) {
12 | const filesWithPath = body.files.map((f) => ({
13 | ...f,
14 | path: `original/${user ? user.id : -1}/${body.job_id}`,
15 | }));
16 | // const imageRequests = await uploader(filesWithPath);
17 | // const pages = filesWithPath.map((f) => {
18 | // let id = uuidV4();
19 | // return {
20 | // id,
21 | // name: `${f.index}.${f.extention}`,
22 | // job_id: body.job_id,
23 | // user_id: req.session.user ? req.user.id : -1,
24 | // created_by: req.user ? req.user.id : -1,
25 | // created_at: db.fn.now(),
26 | // };
27 | // });
28 |
29 | return uploader(filesWithPath)
30 | .then(() => db('job')
31 | .insert({
32 | id: body.job_id,
33 | code: uniqid.time(),
34 | name: body.group_name || uniqid.time(),
35 | lang: body.lang || 'ckb',
36 | status: 'pending',
37 | user_id: user ? user.id : -1,
38 | page_count: body.files.length,
39 | price_per_page: pricePerPage,
40 | from_api: user.from_api ? 1 : 0,
41 | callback: body.callback,
42 | created_at: dayjs.utc().format('YYYY-MM-D H:m:s'),
43 | created_by: user ? user.id : -1,
44 | })
45 | // return db.batchInsert('page', pages, 200);
46 | .then(([id]) => id)
47 | .catch(() => Promise.reject(
48 | new Error('هەڵەیەک رویدا لەکاتی دروستکرنی کردار'),
49 | )))
50 | .catch((e) => Promise.reject(new Error(e.toString())));
51 | }
52 | async function rateJobQuery(body, req) {
53 | const { params } = req;
54 | return db('job')
55 | .update({
56 | rate: body.rate,
57 | })
58 | .where('id', params.job_id)
59 | .then(() => params.job_id);
60 | }
61 | async function readJobListQuery(limit, offset, userId = 0, q = '') {
62 | await db.raw('set transaction isolation level read uncommitted;');
63 |
64 | const query = db('job')
65 | .select(
66 | 'job.id',
67 | 'job.name',
68 | 'job.code',
69 | 'job.user_id',
70 | 'job.page_count',
71 | 'job.paid_page_count',
72 | 'job.user_failing_reason',
73 | 'job.status',
74 | 'job.lang',
75 | 'job.queued_at',
76 | 'job.processed_at',
77 | 'job.finished_at',
78 | 'job.created_at',
79 | 'job.deleted',
80 | 'job.created_by',
81 | 'job.rate',
82 | 'job.from_api',
83 | 'job.callback',
84 | )
85 | .where('user_id', userId)
86 | .andWhereRaw(' created_at BETWEEN NOW() - INTERVAL 30 DAY AND NOW() ')
87 | .orderBy('created_at', 'desc')
88 | .limit(limit)
89 | .offset(offset);
90 |
91 | if (q) {
92 | query.where('job.name', 'LIKE', `%${q}%`);
93 | }
94 | return query;
95 | }
96 | async function readSingleJobQuery(jobId = 0, userId = 0) {
97 | return db('job')
98 | .select(
99 | 'job.id',
100 | 'job.name',
101 | 'job.code',
102 | 'job.user_id',
103 | 'job.page_count',
104 | 'job.paid_page_count',
105 | 'job.user_failing_reason',
106 | 'job.status',
107 | 'job.lang',
108 | 'job.queued_at',
109 | 'job.processed_at',
110 | 'job.finished_at',
111 | 'job.created_at',
112 | 'job.deleted',
113 | 'job.created_by',
114 | 'job.rate',
115 | 'job.from_api',
116 | 'job.callback',
117 | )
118 | .where('user_id', userId)
119 | .andWhere('id', jobId)
120 | .limit(1)
121 | .then(async ([job]) => {
122 | if (job) {
123 | const mutatedJob = job;
124 | const pages = await db('page').select().where('job_id', job.id);
125 | mutatedJob.pages = pages;
126 | return Promise.resolve(mutatedJob);
127 | }
128 | return Promise.resolve(null);
129 | });
130 | }
131 |
132 | module.exports = {
133 | createJobQuery,
134 | rateJobQuery,
135 | readJobListQuery,
136 | readSingleJobQuery,
137 | };
138 |
--------------------------------------------------------------------------------
/query/user.js:
--------------------------------------------------------------------------------
1 | const sha1 = require('sha1');
2 | const { v4: uuidv4 } = require('uuid');
3 | const db = require('../database/connection');
4 |
5 | async function readUserBalanceQuery(userId = 0) {
6 | return db('user_transaction')
7 | .sum('page_count as page_count')
8 | .where('user_id', userId)
9 | .where('confirmed', 1)
10 | .then(([d]) => d);
11 | }
12 | function readTransactionListQuery(userId = 0, limit = 20, offset = 0) {
13 | return db('user_transaction')
14 | .select(
15 | 'user_transaction.type_id',
16 | 'user_transaction.payment_medium_id',
17 | 'user_transaction.page_count',
18 | 'user_transaction.amount',
19 | 'user_transaction.transaction_id',
20 | 'user_transaction.user_note',
21 | 'user_transaction.created_at',
22 | 'payment_medium.name as payment_medium_name',
23 | 'payment_medium.code as payment_medium_code',
24 | 'payment_medium.currency_symbol as payment_medium_currency_symbol',
25 | )
26 | .leftJoin(
27 | 'payment_medium',
28 | 'payment_medium.id',
29 | 'user_transaction.payment_medium_id',
30 | )
31 | .andWhere('user_transaction.user_id', userId)
32 | .andWhere('user_transaction.type_id', 1) // only recharges are considered
33 | .andWhere('user_transaction.confirmed', 1) // only confirmed recharges are considered
34 | .orderBy('user_transaction.id', 'desc')
35 | .limit(limit)
36 | .offset(offset);
37 | }
38 | function readSingleUserQuery(userId = 0) {
39 | return db('user')
40 | .select(
41 | 'id',
42 | 'name',
43 | 'company_name',
44 | 'email',
45 | 'gender',
46 | 'birthdate',
47 | 'phone_no',
48 | 'uid',
49 | 'api_key',
50 | 'can_use_api',
51 | 'monthly_recharge',
52 | )
53 | .where('id', userId)
54 | .then(([data]) => data);
55 | }
56 | function updateUserQuery(body, userId = 0) {
57 | const updateObject = {
58 | name: body.name,
59 | email: body.email,
60 | phone_no: body.phone_no,
61 | company_name: body.company_name,
62 | };
63 | if (body.password) {
64 | const salt = uuidv4();
65 | updateObject.password = sha1(`${salt}${body.password}`);
66 | updateObject.salt = salt;
67 | }
68 | return db('user').update(updateObject).where('id', userId);
69 | }
70 |
71 | module.exports = {
72 | readUserBalanceQuery,
73 | readTransactionListQuery,
74 | readSingleUserQuery,
75 | updateUserQuery,
76 | };
77 |
--------------------------------------------------------------------------------
/routes/auth.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const jwt = require('jsonwebtoken');
3 |
4 | const { v4: uuidv4 } = require('uuid');
5 | const sha1 = require('sha1');
6 | const utc = require('dayjs/plugin/utc');
7 | const dayjs = require('dayjs');
8 |
9 | const allowedMethods = ['bradost'];
10 |
11 | const { createValidator } = require('../middlewares/validators/user');
12 | const {
13 | passwordResetValidator,
14 | requestPasswordResetValidator,
15 | emailPassValidator,
16 | activateAccountValidator,
17 | } = require('../middlewares/validators/auth');
18 | const {
19 | passwordResetTemplate,
20 | activateAccountTemplate,
21 | } = require('../helpers/emailTemplates');
22 | const { send } = require('../helpers/email');
23 |
24 | const db = require('../database/connection');
25 |
26 | dayjs.extend(utc);
27 |
28 | const router = express();
29 |
30 | function register(data) {
31 | const salt = uuidv4();
32 | const user = {
33 | name: data.name,
34 | company_name: data.company_name,
35 | api_key: uuidv4(),
36 | email: data.email,
37 | password: sha1(`${salt}${data.password}`),
38 | salt,
39 | active: 1,
40 | verified: data.method && allowedMethods.indexOf(data.method) > -1 ? 1 : 0,
41 | gender: 'unspecified',
42 | created_at: db.fn.now(),
43 | updated_at: db.fn.now(),
44 | };
45 | return db('user').insert(user);
46 | }
47 | function login(email, password) {
48 | return db.transaction(async (trx) => {
49 | const [checkEmail] = await trx('user')
50 | .select()
51 | .where('email', email)
52 | .andWhere('active', 1)
53 | .andWhere('deleted', 0)
54 | .limit(1);
55 |
56 | if (checkEmail && checkEmail.verified === 0) {
57 | return {
58 | status: 400,
59 | success: false,
60 | msg:
61 | 'هەژمارەکەت کارا نیە تکایە سەیری ئیمێڵەکەت بکە بۆ وەرگرتنی بەستەری کاراکردن یان پەیوەندیمان پێوەبکە.',
62 | user: null,
63 | };
64 | }
65 |
66 | if (!checkEmail || !checkEmail.id) {
67 | return {
68 | status: 400,
69 | success: false,
70 | // msg: ' پۆستی ئەلئکترۆنی نەدۆزرایەوە یان هەژمارەکەت راگیراوە',
71 | msg: 'پۆستی ئەلیکترۆنی یان تێپەڕەوشە هەڵەیە',
72 | user: null,
73 | };
74 | }
75 |
76 | const [checkPassword] = await trx('user')
77 | .select('user.*')
78 | .where('user.id', checkEmail.id)
79 | .andWhere('password', sha1(checkEmail.salt + password))
80 | .limit(1);
81 | if (!checkPassword || !checkPassword.id) {
82 | return {
83 | status: 400,
84 | success: false,
85 | msg: 'پۆستی ئەلیکترۆنی یان تێپەڕەوشە هەڵەیە',
86 | user: null,
87 | };
88 | }
89 | delete checkPassword.salt;
90 | delete checkPassword.password;
91 | delete checkPassword.tmp_password;
92 | const token = jwt.sign({ data: checkPassword }, process.env.USER_JWT_SECRET, { expiresIn: '362d' });
93 |
94 | return {
95 | status: 200,
96 | success: true,
97 | msg: 'سەرکەوتووبوو',
98 | user: checkPassword,
99 | token,
100 | };
101 | });
102 | }
103 | function generateTmpPass(email) {
104 | return db('user')
105 | .select()
106 | .where('email', email)
107 | .where('active', 1)
108 | .where('verified', 1)
109 | .limit(1)
110 | .then(([user]) => {
111 | if (user) {
112 | const mutatedUser = user;
113 | // we will not validate this after 5 mins you must request another one
114 | const tmpPass = sha1(uuidv4());
115 | return db('user')
116 | .update({ tmp_password: tmpPass })
117 | .where('id', mutatedUser.id)
118 | .then(() => {
119 | mutatedUser.tmp_password = tmpPass;
120 | return Promise.resolve(mutatedUser);
121 | });
122 | }
123 | return Promise.reject(new Error('بەکارهێنەر نەدۆزرایەوە'));
124 | });
125 | }
126 |
127 | function passwordReset(data) {
128 | return db('user')
129 | .select()
130 | .where('tmp_password', data.token)
131 | .andWhere('tmp_password', '<>', '')
132 | .limit(1)
133 | .then(([user]) => {
134 | if (user) {
135 | const salt = uuidv4();
136 | const updateObject = {
137 | password: sha1(`${salt}${data.password}`),
138 | salt,
139 | tmp_password: '',
140 | };
141 | return db('user')
142 | .update(updateObject)
143 | .where('id', user.id)
144 | .then(() => Promise.resolve(user));
145 | }
146 | return Promise.reject(
147 | new Error('ئەم بەستەرە بەسەرچووە ناتواندرێت بەکاربهێندرێت'),
148 | );
149 | });
150 | }
151 |
152 | function generateActivationToken(email) {
153 | return db('user')
154 | .select()
155 | .where('email', email)
156 | .limit(1)
157 | .then(([user]) => {
158 | if (user) {
159 | const activationToken = sha1(uuidv4());
160 | const mutatedUser = user;
161 | return Promise.all([
162 | db('user')
163 | .update({ activation_token: activationToken })
164 | .where('id', user.id)
165 | .then(() => {
166 | mutatedUser.activation_token = activationToken;
167 | return Promise.resolve(mutatedUser);
168 | }),
169 | db('activation_token').insert({
170 | token: activationToken,
171 | user_id: mutatedUser.id,
172 | created_at: dayjs.utc().format('YYYY-MM-D H:m:s'),
173 | }),
174 | ]).then((values) => values[0]);
175 | }
176 | return Promise.reject(new Error('بەکارهێنەر نەدۆزرایەوە'));
177 | });
178 | }
179 | function activateAccount(data) {
180 | return db('user')
181 | .select()
182 | .where('activation_token', data.token)
183 | .andWhere('activation_token', '<>', '')
184 | .limit(1)
185 | .then(([user]) => {
186 | if (user) {
187 | const updateObject = {
188 | verified: 1,
189 | activation_token: '',
190 | };
191 | return db('user')
192 | .update(updateObject)
193 | .where('id', user.id)
194 | .then(async () => {
195 | await db('user_transaction').insert({
196 | user_id: user.id,
197 | type_id: 1, // for recharge
198 | payment_medium_id: 1, // for zhir.io
199 | page_count: 50,
200 | confirmed: 1,
201 | amount: 0,
202 | transaction_id: uuidv4(),
203 | user_note:
204 | 'دیاری دروستکردنی هەژمار هەموو مانگێک خۆکارانە ٥٠ لاپەڕە وەردەگریت',
205 | admin_note: 'Account Activation Present',
206 | created_at: db.fn.now(),
207 | });
208 | return Promise.resolve(user);
209 | });
210 | }
211 | return db('activation_token')
212 | .select()
213 | .where('token', data.token)
214 | .limit(1)
215 | .then(([userToken]) => {
216 | if (userToken) {
217 | return Promise.resolve(userToken);
218 | }
219 | return Promise.reject(
220 | new Error(
221 | 'ئەم بەستەرە بەسەرچووە ناتواندرێت بەکاربهێندرێت',
222 | ),
223 | );
224 | });
225 | });
226 | }
227 |
228 | router.post('/login', emailPassValidator, (req, res) => {
229 | const { email, password } = req.body;
230 | login(email, password)
231 | .then((r) => {
232 | if (r.success) {
233 | res.json({
234 | ...r.user,
235 | token: r.token,
236 | });
237 | } else {
238 | res.status(r.status).json({ msg: r.msg });
239 | }
240 | })
241 | .catch(() => {
242 | res.status(401).json({
243 | msg: 'هەڵەیەک ڕوویدا لەکاتی چوونەژوورەوە',
244 | });
245 | });
246 | });
247 | router.post('/register', createValidator, (req, res) => {
248 | const { body } = req;
249 | register(body)
250 | .then(async ([userId]) => {
251 | if (allowedMethods.indexOf(body.method) > -1) {
252 | await db('user_transaction').insert({
253 | user_id: userId,
254 | type_id: 1, // for recharge
255 | payment_medium_id: 1, // for zhir.io
256 | page_count: 50,
257 | confirmed: 1,
258 | amount: 0,
259 | transaction_id: uuidv4(),
260 | user_note:
261 | 'دیاری دروستکردنی هەژمار هەموو مانگێک خۆکارانە ٥٠ لاپەڕە وەردەگریت',
262 | admin_note: 'Account Activation Present',
263 | created_at: db.fn.now(),
264 | });
265 | }
266 | generateActivationToken(body.email)
267 | .then((user) => {
268 | const activationToken = user.activation_token;
269 | const url = `${process.env.DOMAIN}/api/auth/activate-account?token=${activationToken}`;
270 | const htmlMsg = activateAccountTemplate(url);
271 | send('ژیر | کاراکردنی هەژمار', htmlMsg, user.email, () => {
272 | res.json({ msg: 'هەژمار دروستکرا' });
273 | });
274 | })
275 | .catch(() => {
276 | res.status(500).json({
277 | msg:
278 | 'ناردنی بەستەری کاراکردنی هەژمار سەرکەوتوو نەبوو تکایە پەیوەندیمان پێوەبکە',
279 | });
280 | });
281 | })
282 | .catch(() => {
283 | res.status(500).json({
284 | msg: 'خۆتۆمارکردن سەرکەوتوو نەبوو',
285 | });
286 | });
287 | });
288 | router.post(
289 | '/request-password-recovery',
290 | requestPasswordResetValidator,
291 | (req, res) => {
292 | const { body } = req;
293 | generateTmpPass(body.email)
294 | .then((user) => {
295 | const tmpPass = user.tmp_password;
296 | const url = `${process.env.DOMAIN}/auth/password-recovery?token=${tmpPass}`;
297 | const htmlMsg = passwordResetTemplate(url);
298 | send('ژیر | گۆڕینی تێپەڕەوشە', htmlMsg, user.email, () => {
299 | res.json({ msg: 'yayy' });
300 | });
301 | })
302 | .catch(() => {
303 | res.status(500).json({
304 | msg: 'ناردنی تێپەڕەوشە سەرکەوتوونەبوو',
305 | });
306 | });
307 | },
308 | );
309 | router.post('/password-recovery', passwordResetValidator, (req, res) => {
310 | const { body } = req;
311 | passwordReset(body)
312 | .then((user) => {
313 | const mutatedUser = {
314 | ...user,
315 | salt: undefined,
316 | password: undefined,
317 | tmp_password: undefined,
318 | };
319 | const token = jwt.sign({ data: mutatedUser }, process.env.USER_JWT_SECRET, { expiresIn: '362d' });
320 | mutatedUser.token = token;
321 | res.json(mutatedUser);
322 | })
323 | .catch((e) => {
324 | res.status(400).json({
325 | msg: e.toString(),
326 | });
327 | });
328 | });
329 |
330 | // this route should be called and generated once per account only in registration
331 | // router.post(
332 | // '/request-activation-link',
333 | // requestPasswordResetValidator,
334 | // (req, res) => {
335 | // const { body } = req;
336 | // generateActivationToken(body.email)
337 | // .then((user) => {
338 | // const activationToken = user.activation_token;
339 | // const url = `${process.env.DOMAIN}/activate-account?token=${activationToken}`;
340 | // const htmlMsg = activateAccountTemplate(url);
341 | // send('ژیر | کاراکردنی هەژمار', htmlMsg, user.email, () => {
342 | // res.json({ msg: 'yayy' });
343 | // });
344 | // })
345 | // .catch((e) => {
346 | // res.status(500).json({
347 | // msg: 'ناردنی بەستەری کاراکردنی هەژمار سەرکەوتوو نەبوو تکایە پەیوەندیمان پێوەبکە',
348 | // });
349 | // });
350 | // }
351 | // );
352 |
353 | router.get('/activate-account', activateAccountValidator, (req, res) => {
354 | const { query } = req;
355 | activateAccount(query)
356 | .then((user) => {
357 | if (user.user_id) {
358 | res.status(200).json({ state: 'already-activated' });
359 | } else {
360 | res.status(200).json({ state: 'activated' });
361 | }
362 | })
363 | .catch((e) => {
364 | res.status(500).json({
365 | msg: e.toString(),
366 | });
367 | });
368 | });
369 |
370 | // router.get('/logout', (req, res) => {
371 | // req.session.destroy(() => {
372 | // res.redirect('/');
373 | // });
374 | // });
375 | module.exports = router;
376 |
--------------------------------------------------------------------------------
/routes/fastpay.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const superagent = require('superagent');
3 |
4 | const md5 = require('md5');
5 | const customerJWTValidator = require('../middlewares/jwt/user');
6 | const {
7 | fastpayRechargeValidator,
8 | } = require('../middlewares/validators/fastpay');
9 | const db = require('../database/connection');
10 |
11 | const router = express.Router();
12 |
13 | /* -------------------- Fastpay Related Functions----------------- */
14 | const fastpayPaymentMediumId = 2;
15 | const fastpayTransactionTypeId = 1; // Balance Recharge
16 |
17 | async function generateDraftTransaction(customerId = 0, body = {}) {
18 | const pages = {
19 | 8000: 100,
20 | 30000: 500,
21 | 50000: 1000,
22 | };
23 | return db('user_transaction').insert({
24 | type_id: fastpayTransactionTypeId,
25 | payment_medium_id: fastpayPaymentMediumId,
26 | user_id: customerId,
27 | page_count: pages[body.amount] || 0,
28 | amount: body.amount,
29 | transaction_id: -1,
30 | user_note: 'پڕکردنەوەی باڵانس',
31 | confirmed: 0, // this is eqevelant to draft
32 | created_at: db.fn.now(),
33 | created_by: customerId,
34 |
35 | }).then(([id]) => id)
36 | .catch(() => -1);
37 | }
38 |
39 | async function updateDraftTransaction(transactionId = 0, paymentTransactionId, customerAccountNo = '') {
40 | return db('user_transaction').update({
41 | transaction_id: paymentTransactionId,
42 | admin_note: `Via Fastpay Account:${customerAccountNo}`,
43 | }).where(db.raw('md5(id)'), transactionId).then(() => 'Success')
44 | .catch(() => '/payment/fastpay/fail');
45 | }
46 |
47 | async function loadSingleTransaction(transactionId = 0) {
48 | // transactionId is md5 hashed due to new fastpay credentials
49 | return db('user_transaction').select()
50 | .where(db.raw('md5(id)'), transactionId)
51 | .limit(1)
52 | .then(([transaction]) => transaction);
53 | }
54 | async function completeDraftTransaction(transactionId = 0, fastpayTransactionId = 0) {
55 | return db('user_transaction')
56 | .update({
57 | confirmed: 1,
58 | })
59 | .where(db.raw('md5(id)'), transactionId)
60 | .andWhere('transaction_id', fastpayTransactionId)
61 | .then(() => transactionId);
62 | }
63 |
64 | async function initFastpayTransaction(orderId = '0', amount = '0') {
65 | return superagent.post(`${process.env.FASTPAY_API_URL}/api/v1/public/pgw/payment/initiation`)
66 | .set('Content-Type', 'application/json')
67 | .send({
68 | store_id: process.env.FASTPAY_STORE_ID,
69 | store_password: process.env.FASTPAY_MERCHANT_STORE_PASS,
70 | order_id: md5(orderId),
71 | cart: JSON.stringify([
72 | {
73 | name: 'Account Recharge',
74 | qty: 1,
75 | unit_price: amount,
76 | sub_total: amount,
77 | },
78 | ]),
79 | bill_amount: Math.round(amount),
80 | currency: 'IQD',
81 | })
82 | .then((res) => {
83 | const { body } = res;
84 | if (body.code === 200) {
85 | return body.data.redirect_uri;
86 | }
87 | return -1;
88 | })
89 | .catch(() => -1);
90 | }
91 | async function validateFastpayTransaction(customerTransactionId = '0') {
92 | return superagent.post(`${process.env.FASTPAY_API_URL}/api/v1/public/pgw/payment/validate`)
93 | .set('Content-Type', 'application/json')
94 | .send({
95 | store_id: process.env.FASTPAY_STORE_ID,
96 | store_password: process.env.FASTPAY_MERCHANT_STORE_PASS,
97 | order_id: customerTransactionId,
98 | })
99 | .then(async (res) => {
100 | const { body } = res;
101 |
102 | if (`${body.code}` === '200') {
103 | await completeDraftTransaction(customerTransactionId, body.data.transaction_id);
104 | }
105 | return 1;
106 | })
107 | .catch(() => -1);
108 | }
109 |
110 | /* ----------------------------------------------------------------- */
111 |
112 | router.all('/fail', async (req, res) => {
113 | // we can remove the draft transaction
114 | res.redirect(`${process.env.DOMAIN}?recharge-status=fail`);
115 | });
116 | router.all('/success', (req, res) => {
117 | res.redirect(`${process.env.DOMAIN}?recharge-status=success`);
118 | });
119 | router.all('/cancel', (req, res) => {
120 | res.redirect(`${process.env.DOMAIN}?recharge-status=fail`);
121 | });
122 |
123 | router.post('/ipn', async (req, res) => {
124 | /*
125 | gw_transaction_id: CUL1NUB731,
126 | merchant_order_id: 1,
127 | received_amount: 1000,
128 | currency: IQD,
129 | status: Success,
130 | customer_name: Aram Rafeq,
131 | customer_mobile_number: +9647507665935,
132 | at: 2020-11-26 13:54:01
133 | */
134 | const { body } = req;
135 | const {
136 | gw_transaction_id: fastpayTransactionId,
137 | merchant_order_id: customerTransactionId,
138 | received_amount: billAmount,
139 | customer_mobile_number: fastpayCustomerAccountno,
140 | status,
141 | } = body;
142 | if (status === 'Success') {
143 | const transaction = await loadSingleTransaction(customerTransactionId);
144 | if (transaction) {
145 | if (Math.abs(Math.round(transaction.amount) - Math.round(billAmount)) < 10) {
146 | await updateDraftTransaction(
147 | customerTransactionId,
148 | fastpayTransactionId,
149 | fastpayCustomerAccountno,
150 | );
151 |
152 | await validateFastpayTransaction(customerTransactionId);
153 | res.send('draft transaction updated');
154 | } else {
155 | res.send('transaction bill fails to validate');
156 | }
157 | } else {
158 | res.send('transaction fails');
159 | }
160 | } else {
161 | res.send('payment failed');
162 | }
163 | });
164 | router.get('/recharge', customerJWTValidator, fastpayRechargeValidator, async (req, res) => {
165 | const transactionId = await generateDraftTransaction(req.user.id, req.query);
166 | const paymentURL = await initFastpayTransaction(`${transactionId}`, parseInt(req.query.amount, 10));
167 | if (transactionId > -1) {
168 | if (typeof paymentURL === 'number' && paymentURL === -1) {
169 | res.redirect('/payment/fastpay/fail');
170 | } else {
171 | res.redirect(paymentURL);
172 | }
173 | } else {
174 | res.redirect('/payment/fastpay/fail');
175 | }
176 | // res.json({ msg: 'hello world' });
177 | });
178 |
179 | module.exports = router;
180 |
--------------------------------------------------------------------------------
/routes/idPay.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable eqeqeq */
2 | const express = require('express');
3 | const superagent = require('superagent');
4 | const userJWT = require('../middlewares/jwt/user');
5 | const { payValidator, verifyValidator } = require('../middlewares/validators/idPay');
6 |
7 | const db = require('../database/connection');
8 |
9 | let agent = superagent.agent();
10 | agent = agent.set('Content-Type', 'application/json');
11 | agent = agent.set('X-API-KEY', process.env.IDPAY_AUTH_KEY);
12 | if (process.env.IDPAY_SANDBOX === 1) {
13 | agent = agent.set('X-SANDBOX', 1);
14 | }
15 |
16 | function generateTransaction(object = {}) {
17 | return db('user_transaction')
18 | .insert(object)
19 | .then(([d]) => d);
20 | }
21 | function setPaymentTransactionId(id, paymentTransactionId) {
22 | return db('user_transaction')
23 | .update({ transaction_id: paymentTransactionId })
24 | .where('id', id)
25 | .then(() => id);
26 | }
27 | function confirmTransaction(id, paymentTransactionId) {
28 | return db('user_transaction')
29 | .update({ confirmed: 1, admin_note: 'confirmed' })
30 | .where('id', id)
31 | .andWhere('transaction_id', paymentTransactionId)
32 | .then(() => db('user')
33 | .select('user.*')
34 | .innerJoin(
35 | 'user_transaction',
36 | 'user_transaction.user_id',
37 | 'user.id',
38 | )
39 | .andWhere(
40 | 'user_transaction.transaction_id',
41 | paymentTransactionId,
42 | )
43 | .andWhere('user_transaction.id', id)
44 | .andWhere('user.active', 1)
45 | .andWhere('user.deleted', 0)
46 | .limit(1));
47 | }
48 | // offical currency is rial which have one more 0 that tuman
49 | const prices = {
50 | 400000: 50,
51 | 700000: 100,
52 | 2500000: 500,
53 | 4000000: 1000,
54 | };
55 | const router = express();
56 | router.post('/verify', verifyValidator, (req, res) => {
57 | // Transaction Status link https://idpay.ir/web-service/v1.1/?javascript#ad39f18522
58 | const { body } = req;
59 | if (body.status === 10) {
60 | // do transaction confirmation here
61 | agent
62 | .post('https://api.idpay.ir/v1.1/payment/verify')
63 | .send({
64 | id: body.id,
65 | order_id: body.order_id,
66 | })
67 | .end(async (err) => {
68 | if (!err) {
69 | const [user] = await confirmTransaction(
70 | body.order_id,
71 | body.id,
72 | );
73 | if (user) {
74 | delete user.salt;
75 | delete user.password;
76 | delete user.tmp_password;
77 | req.session.user = user;
78 | res.redirect('/app/balance?success=1');
79 | } else {
80 | res.redirect('/app/balance?fail=1');
81 | }
82 | } else {
83 | res.json({
84 | msg: 'هەڵەیەک رویدا لەکاتی دڵنیابوونەوە لە پارەدان',
85 | });
86 | }
87 | });
88 | } else if (body.status == 1) {
89 | res.json({ msg: 'Payment not maid' });
90 | } else if (body.status == 2) {
91 | res.json({ msg: 'Payment failed' });
92 | } else if (body.status == 3) {
93 | res.json({ msg: 'An error has occurred' });
94 | } else if (body.status == 4) {
95 | res.json({ msg: 'Blocked' });
96 | } else if (body.status == 5) {
97 | res.json({ msg: 'Return to payer' });
98 | } else if (body.status == 6) {
99 | res.json({ msg: 'Reversed system' });
100 | } else if (body.status == 7) {
101 | res.redirect('/app/recharge');
102 | } else if (body.status == 8) {
103 | res.json({ msg: 'Moved to payment gateway' });
104 | } else if (body.status == 100) {
105 | res.redirect('/app/balance');
106 | } else if (body.status == 101) {
107 | res.json({ msg: 'Payment has already been approved' });
108 | } else if (body.status == 200) {
109 | res.json({ msg: 'Deposited to the recipient' });
110 | }
111 | });
112 | router.get('/pay/:amount', userJWT, payValidator, async (req, res) => {
113 | const { params } = req;
114 | const { user } = req.session;
115 | const orderId = await generateTransaction({
116 | user_id: user.id,
117 | type_id: 1,
118 | payment_medium_id: 6, // IDPay medium
119 | page_count: prices[params.amount],
120 | amount: params.amount,
121 | transaction_id: null,
122 | created_by: user.id,
123 | confirmed: 0,
124 | created_at: db.fn.now(),
125 | admin_note: 'draft',
126 | });
127 | agent
128 | .post('https://api.idpay.ir/v1.1/payment')
129 | .send({
130 | order_id: orderId,
131 | amount: params.amount,
132 | name: user.name,
133 | phone: user.phone_no,
134 | mail: user.email,
135 | desc: ' ',
136 | callback: `${process.env.DOMAIN}/api/payment/idpay/verify`,
137 | })
138 | .end(async (err, r) => {
139 | if (!err) {
140 | const { body } = r;
141 | await setPaymentTransactionId(orderId, body.id);
142 | res.redirect(body.link);
143 | } else {
144 | res.json({
145 | msg:
146 | 'هەڵەیەک رویدا لەکاتی جێبەجێکردنی کردارەکان، دووبارە هەوڵبدەرەوە',
147 | });
148 | }
149 | });
150 | });
151 |
152 | module.exports = router;
153 |
--------------------------------------------------------------------------------
/routes/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | // const withUserAuth = require('../controller/withUserAuth');
3 | const jwtVerify = require('../middlewares/jwt/user');
4 |
5 | const authRouter = require('./auth');
6 | const jobRouter = require('./job');
7 | const s3interface = require('./s3interface');
8 | const userRouter = require('./user');
9 | const idPayRouter = require('./idPay');
10 | const fastpayRouter = require('./fastpay');
11 |
12 | const router = express();
13 | router.use('/auth', authRouter);
14 | router.use('/job', jobRouter);
15 | router.use('/user', jwtVerify, userRouter);
16 | router.use('/payment/fastpay', fastpayRouter);
17 | router.use('/payment/idpay', idPayRouter);
18 | // router.use('/assets', withUserAuth(s3interface));
19 | router.use('/assets', s3interface);
20 |
21 | module.exports = router;
22 |
--------------------------------------------------------------------------------
/routes/job.js:
--------------------------------------------------------------------------------
1 | // import { v4: uuidV4 } from 'uuid';
2 |
3 | const express = require('express');
4 |
5 | const {
6 | createValidator,
7 | readSingleJobValidator,
8 | rateJobValidator,
9 | createExternalValidator,
10 | } = require('../middlewares/validators/job');
11 | const {
12 | createJobQuery,
13 | rateJobQuery,
14 | readJobListQuery,
15 | readSingleJobQuery,
16 | } = require('../query/job');
17 |
18 | const userJWT = require('../middlewares/jwt/user');
19 | const isValidAPIKey = require('../helpers/isValidAPIKey');
20 |
21 | const router = express();
22 |
23 | router.post('/', userJWT, createValidator, async (req, res) => {
24 | createJobQuery(req.body, req.user)
25 | .then(() => {
26 | res.json({
27 | msg: 'کارەکە بە سەرکەوتووی نێردرا',
28 | });
29 | })
30 | .catch(() => {
31 | res.status(500).json({
32 | msg: 'هەڵەیەک لە ڕاژە ڕوویدا',
33 | });
34 | });
35 | });
36 | router.post(
37 | '/external',
38 | isValidAPIKey,
39 | createExternalValidator,
40 | async (req, res) => {
41 | createJobQuery(req.body, req.user)
42 | .then(() => {
43 | res.status(200).json({
44 | success: true,
45 | status: 200,
46 | msg: 'کارەکە بە سەرکەوتووی نێردرا',
47 | });
48 | })
49 | .catch((e) => {
50 | res.status(500).json({
51 | success: false,
52 | status: 500,
53 | msg: e.toString(),
54 | });
55 | });
56 | },
57 | );
58 |
59 | router.get('/external/list', isValidAPIKey, (req, res) => {
60 | readJobListQuery(100, 0, req.user.id, req.query.q)
61 | .then((data) => {
62 | res.json(data);
63 | })
64 | .catch(() => {
65 | res.status(500).json({
66 | msg: 'هەڵەیەک ڕوویدا لەکاتی گەراندنەوەی فایلەکان',
67 | });
68 | });
69 | });
70 | router.get(
71 | '/external/:job_id',
72 | isValidAPIKey,
73 | readSingleJobValidator,
74 | (req, res) => {
75 | readSingleJobQuery(req.params.job_id, req.user.id)
76 | .then((data) => {
77 | if (data) {
78 | res.json(data);
79 | } else {
80 | res.status(404).json({
81 | msg: 'کرداری داواکراو نەدۆزرایەوە',
82 | });
83 | }
84 | })
85 | .catch(() => {
86 | res.status(500).json({
87 | msg: 'هەڵەیەک ڕوویدا لەکاتی گەراندنەوەی فایلەکان',
88 | });
89 | });
90 | },
91 | );
92 | router.put(
93 | '/rate/:job_id',
94 | userJWT,
95 | rateJobValidator,
96 | async (req, res) => {
97 | rateJobQuery(req.body, req)
98 | .then(() => {
99 | res.json({
100 | msg: 'کارەکە بە سەرکەوتووی نێردرا',
101 | });
102 | })
103 | .catch(() => {
104 | res.status(500).json({
105 | msg: 'هەڵەیەک لە ڕاژە ڕوویدا',
106 | });
107 | });
108 | },
109 | );
110 | router.get('/list', userJWT, (req, res) => {
111 | readJobListQuery(100, 0, req.user.id, req.query.q)
112 | .then((data) => {
113 | res.json(data);
114 | })
115 | .catch(() => {
116 | res.status(500).json({
117 | msg: 'هەڵەیەک ڕوویدا لەکاتی گەراندنەوەی فایلەکان',
118 | });
119 | });
120 | });
121 |
122 | router.get('/:job_id', userJWT, readSingleJobValidator, (req, res) => {
123 | readSingleJobQuery(req.params.job_id, req.user.id)
124 | .then((data) => {
125 | if (data) {
126 | res.json(data);
127 | } else {
128 | res.status(404).json({ msg: 'کرداری داواکراو نەدۆزرایەوە' });
129 | }
130 | })
131 | .catch(() => {
132 | res.status(500).json({
133 | msg: 'هەڵەیەک ڕوویدا لەکاتی گەراندنەوەی فایلەکان',
134 | });
135 | });
136 | });
137 |
138 | module.exports = router;
139 |
--------------------------------------------------------------------------------
/routes/s3interface.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const pipeS3ObjectToExpress = require('../helpers/aws/pipeS3ObjectToExpress');
3 |
4 | const router = express.Router();
5 |
6 | router.get('*', (req, res) => {
7 | const rawKey = `${req.url}`.substring(1);
8 | const key = rawKey.split('?')[0];
9 | // const splitedKey = key.split('/');
10 | // const userId = splitedKey[1] || -1;
11 | const params = {
12 | Bucket: process.env.AWS_BUCKET,
13 | Key: key,
14 | };
15 | pipeS3ObjectToExpress(params, res);
16 | // if (userId === req.user.id || req.user.is_admin) {
17 | // const params = {
18 | // Bucket: process.env.AWS_BUCKET,
19 | // Key: key,
20 | // };
21 | // pipeS3ObjectToExpress(params, res);
22 | // } else if (userId > -1) {
23 | // res.json({ msg: 'مافی گەشتن بەم فایلەت نیە' });
24 | // } else {
25 | // res.json({ msg: 'فایلی داواکراو بوونی نیە یان نەدۆزرایەوە.' });
26 | // }
27 | });
28 | module.exports = router;
29 |
--------------------------------------------------------------------------------
/routes/user.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 |
3 | const { updateValidator } = require('../middlewares/validators/user');
4 | const {
5 | readUserBalanceQuery,
6 | readTransactionListQuery,
7 | readSingleUserQuery,
8 | updateUserQuery,
9 | } = require('../query/user');
10 |
11 | const router = express();
12 | router.put('/', updateValidator, (req, res) => {
13 | updateUserQuery(req.body, req.user.id)
14 | .then(() => {
15 | res.json({ msg: 'پرۆفایلەکەت بە سەرکەوتووی نوێکرایەوە' });
16 | })
17 | .catch(() => {
18 | res.status(500).json({
19 | msg: 'هەڵەیەک ڕوویدا لە کاتی نوێکردنەوەی پرۆفایلەکەت',
20 | });
21 | });
22 | });
23 |
24 | router.get('/', (req, res) => {
25 | readSingleUserQuery(req.user.id)
26 | .then((data) => {
27 | res.json(data);
28 | })
29 | .catch(() => {
30 | res.status(500).json({
31 | msg: 'هەڵەیەک ڕوویدا لەکاتی وەرگرتنەوەی زانیاری بەکارهێنەر',
32 | });
33 | });
34 | });
35 | router.get('/balance', (req, res) => {
36 | readUserBalanceQuery(req.user.id)
37 | .then((data) => {
38 | res.json(data);
39 | })
40 | .catch(() => {
41 | res.status(500).json({
42 | msg: 'هەڵەیەک ڕوویدا لەکاتی گەراندنەوەی باڵانسی بەکارهێنەر',
43 | });
44 | });
45 | });
46 | router.get('/transactions', (req, res) => {
47 | readTransactionListQuery(req.user.id, 20, 0)
48 | .then((data) => {
49 | res.json(data);
50 | })
51 | .catch(() => {
52 | res.status(500).json({
53 | msg: 'هەڵەیەک رویدا لەکاتی وەرگرتنەوەی دواین پارەدانەکان',
54 | });
55 | });
56 | });
57 |
58 | module.exports = router;
59 |
--------------------------------------------------------------------------------