├── config └── dbconfig.js ├── .gitignore ├── package.json ├── index.js ├── jobs └── update-market-data.js ├── utils └── tatum.js ├── routes ├── tickets.js ├── payments.js └── users.js └── models ├── referrals.js ├── market_data.js ├── ticket.js └── user.js /config/dbconfig.js: -------------------------------------------------------------------------------- 1 | export default { 2 | url:'mongodb://localhost/shopify' 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | yarn.lock 4 | nginx-default 5 | /metadata 6 | package-lock.json 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ticket-backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "start": "node index.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@sendgrid/mail": "^7.7.0", 15 | "@tatumio/tatum": "^1.37.31", 16 | "axios": "^1.3.4", 17 | "bcrypt": "^5.1.0", 18 | "cors": "^2.8.5", 19 | "dotenv": "^16.0.3", 20 | "express": "^4.18.2", 21 | "fecha": "^4.2.3", 22 | "jsonwebtoken": "^9.0.0", 23 | "mysql": "^2.18.1", 24 | "node-cron": "^3.0.2", 25 | "node-fetch": "^3.3.0", 26 | "nodemailer": "^6.9.1", 27 | "nodemon": "^2.0.21", 28 | "stripe": "^11.13.0", 29 | "voucher-code-generator": "^1.3.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import pkg from 'body-parser'; 3 | import user_router from './routes/users.js'; 4 | import * as path from 'path'; 5 | import { fileURLToPath } from 'url'; 6 | import mysql from 'mysql'; 7 | import cors from 'cors'; 8 | import updateMarketData from './jobs/update-market-data.js'; 9 | import cron from 'node-cron'; 10 | import payment_router from './routes/payments.js'; 11 | import tickets_router from './routes/tickets.js'; 12 | 13 | const __filename = fileURLToPath(import.meta.url); 14 | const __dirname = path.dirname(__filename); 15 | 16 | const { json } = pkg; 17 | const app = express(); 18 | const port = 2479; 19 | 20 | const connection = mysql.createConnection({ 21 | host: 'localhost', 22 | user: 'alek_db', 23 | password: '(&zjn$#2Z', 24 | database: 'tickets' 25 | }); 26 | 27 | connection.connect((err) => { 28 | if (err) throw err; 29 | console.log('Connected to MySQL Server!'); 30 | }); 31 | 32 | app.use(json()); 33 | app.use(cors()); 34 | 35 | app.use('/api/users', user_router); 36 | app.use('/api/payment', payment_router); 37 | app.use('/api/ticket', tickets_router); 38 | 39 | cron.schedule('0 * * * *', () => { 40 | updateMarketData(); 41 | }); 42 | 43 | app.listen( 44 | port, 45 | () => console.log(`app listening at http://localhost:${port}`) 46 | ); -------------------------------------------------------------------------------- /jobs/update-market-data.js: -------------------------------------------------------------------------------- 1 | 2 | import axios from 'axios'; 3 | import * as path from 'path'; 4 | import { fileURLToPath } from 'url'; 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = path.dirname(__filename); 7 | import dotenv from 'dotenv'; 8 | dotenv.config({path:path.resolve(__dirname, '../.env')}); 9 | import { getMarketData, insertMarketData, updateMarketData } from '../models/market_data.js'; 10 | 11 | const COINMARKETCAP_BTC_ID = 1; 12 | const COINMARKETCAP_CAP_ETH = 1027; 13 | const COINMARKETCAP_CAP_SOL = 5426; 14 | const COINMARKETCAP_CAP_BNB = 1839; 15 | const COINMARKETCAP_CAP_ADA = 2010; 16 | const COINMARKECAP_CAP_MATIC = 3890; 17 | 18 | const COIN_ARRAYS = [ 19 | // COINMARKETCAP_BTC_ID, 20 | COINMARKETCAP_CAP_ETH, 21 | // COINMARKETCAP_CAP_SOL, 22 | // COINMARKETCAP_CAP_BNB, 23 | // COINMARKETCAP_CAP_ADA, 24 | COINMARKECAP_CAP_MATIC 25 | ]; 26 | 27 | const updateMarketDataJob = () => { 28 | let response = null; 29 | COIN_ARRAYS.forEach(async capID => { 30 | try { 31 | response = await axios.get('https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?id=' + capID, { 32 | headers: { 33 | 'X-CMC_PRO_API_KEY': process.env.COINMARKETCAP_API_KEY, 34 | }, 35 | }); 36 | } catch (ex) { 37 | response = null; 38 | console.log(ex); 39 | } 40 | if (response) { 41 | const json = response.data.data; 42 | const data = json[capID.toString()]; 43 | console.log('Updated ' + data.symbol + " data"); 44 | updateMarketData(capID, data); 45 | // var market_data = await getMarketData(data.id); 46 | // if (market_data != undefined) { 47 | // updateMarketData(capID, data); 48 | 49 | // } else { 50 | // insertMarketData(data); 51 | // } 52 | } 53 | }) 54 | } 55 | 56 | export default updateMarketDataJob; 57 | -------------------------------------------------------------------------------- /utils/tatum.js: -------------------------------------------------------------------------------- 1 | import { mintNFTWithUri, EthMintErc721, ipfsUpload, createNFT } from '@tatumio/tatum'; 2 | import * as path from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | const __filename = fileURLToPath(import.meta.url); 5 | const __dirname = path.dirname(__filename); 6 | import dotenv from 'dotenv'; 7 | import fs from 'fs'; 8 | 9 | const TicketChain = process.env.TICKET_CHAIN; 10 | const ContractAddress = process.env.CONTRACT_ADDRESS; 11 | const MinterAddress = process.env.MINTER_ADDRESS; 12 | const OwnerAddress = process.env.OWNER_WALLET_ADDRESS; 13 | 14 | export const uploadMetadata = async function (token_id, ipfsURL) { 15 | return new Promise(async (resolve, reject) => { 16 | try { 17 | const metadata = { 18 | name: "The Haven Wood Ticket #" + token_id, 19 | description: "This is a ticket to see The Haven Wood movie", 20 | image: ipfsURL, 21 | tokenId: token_id 22 | }; 23 | const jsonString = JSON.stringify(metadata); 24 | console.log(jsonString); 25 | var metadataJsonFile = './metadata/' + token_id + ".json"; 26 | fs.writeFileSync(metadataJsonFile, jsonString); 27 | 28 | var buffer = fs.readFileSync(metadataJsonFile); 29 | 30 | var ipfsHash = await ipfsUpload(buffer, token_id + ".json"); 31 | return resolve(ipfsHash); 32 | } catch (err) { 33 | return reject(err); 34 | } 35 | }) 36 | }; 37 | 38 | export const mintNFTwithMetadata = async function (token_id, ipfsURL) { 39 | return new Promise(async (resolve, reject) => { 40 | var mintRequest = new EthMintErc721(); 41 | mintRequest.chain = TicketChain; 42 | mintRequest.contractAddress = ContractAddress; 43 | mintRequest.minter = MinterAddress; 44 | mintRequest.to = OwnerAddress; 45 | mintRequest.tokenId = token_id; 46 | //mintRequest.url = ipfsURL; 47 | try { 48 | var uploadResp = await uploadMetadata(token_id, ipfsURL); 49 | console.log(' >>> ipfsUpload response >>>', uploadResp); 50 | 51 | mintRequest.url = 'https://ipfs.io/ipfs/' + uploadResp.ipfsHash; 52 | console.log(mintRequest.url); 53 | 54 | var resp = await mintNFTWithUri(process.env.IS_TESTNET, mintRequest); 55 | console.log(' >>> mintNFTWithUri response >>>', resp); 56 | return resolve(resp.txId); 57 | } catch (err) { 58 | return reject(err); 59 | } 60 | }) 61 | }; -------------------------------------------------------------------------------- /routes/tickets.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import * as path from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | import { getTicketSummary, insertTicketSummary, updateTicketSummary, getAllTickets, getUserTickets } from '../models/ticket.js'; 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = path.dirname(__filename); 7 | import dotenv from 'dotenv'; 8 | dotenv.config({ path: path.resolve(__dirname, '../.env') }); 9 | import { mintNFTwithMetadata, uploadMetadata } from '../utils/tatum.js'; 10 | 11 | var tickets_router = express.Router(); 12 | 13 | tickets_router.get('/user/:id', async (req, res) => { 14 | console.log('Get all tickets', req.params); 15 | try { 16 | var userTickets = await getUserTickets(req.params.id); 17 | return res 18 | .status(200) 19 | .json({ tickets: userTickets }); 20 | } catch (e) { 21 | // Display error on client 22 | return res 23 | .status(500) 24 | .json({ error: e.message }); 25 | } 26 | }); 27 | 28 | tickets_router.get('/current_price', async (req, res) => { 29 | console.log('Get current ticket price'); 30 | try { 31 | var ticket_summary = await getTicketSummary(); 32 | return res 33 | .status(200) 34 | .json({ ticket_price: ticket_summary.current_price }); 35 | } catch (e) { 36 | // Display error on client 37 | return res 38 | .status(500) 39 | .json({ error: e.message }); 40 | } 41 | }); 42 | 43 | tickets_router.post('/testTicketMint', async(req, res) => { 44 | console.log('testTicketMint', req.body); 45 | try { 46 | var resp = await mintNFTwithMetadata(req.body.token_id, req.body.url); 47 | return res 48 | .status(200) 49 | .json({ result: resp }); 50 | } catch (err) { 51 | // Display error on client 52 | console.log('testTicketMint error', err.response.data); 53 | return res 54 | .status(err.response.data.statusCode) 55 | .json({ error: err.response.data }); 56 | } 57 | }); 58 | 59 | tickets_router.post('/testUploadMetadata', async(req, res) => { 60 | console.log('testUploadMetadata', req.body); 61 | try { 62 | var ipfsHash = await uploadMetadata(req.body.token_id, req.body.url); 63 | return res 64 | .status(200) 65 | .json(ipfsHash); 66 | } catch (err) { 67 | // Display error on client 68 | console.log('testUploadMetadata error', err.response.data); 69 | return res 70 | .status(err.response.data.statusCode) 71 | .json({ error: err.response.data }); 72 | } 73 | }); 74 | 75 | export default tickets_router; 76 | -------------------------------------------------------------------------------- /models/referrals.js: -------------------------------------------------------------------------------- 1 | import mysql from 'mysql'; 2 | import { format } from 'fecha'; 3 | 4 | var pool = mysql.createPool({ 5 | connectionLimit: 100, 6 | host: 'localhost', 7 | user: 'alek_db', 8 | password: '(&zjn$#2Z', 9 | database: 'tickets', 10 | timezone: 'Z' 11 | }); 12 | 13 | export const getReferralCodes = async function (userId) { 14 | return new Promise((resolve, reject) => { 15 | var sql = "SELECT * FROM referrals WHERE user_id = ?"; 16 | pool.getConnection(function (err, connection) { 17 | if (err) { 18 | console.log(err); 19 | return reject(err); 20 | } 21 | 22 | connection.query(sql, [userId], function (err, results) { 23 | connection.release(); 24 | if (err) { 25 | console.log(err); 26 | return reject(err); 27 | } 28 | return resolve(results); 29 | }); 30 | }); 31 | }) 32 | }; 33 | 34 | export const insertReferralCode = async function (user_id, referral_code) { 35 | return new Promise((resolve, reject) => { 36 | var sql = "INSERT INTO referrals (user_id, referral_code) VALUES (?, ?)"; 37 | pool.getConnection(function (err, connection) { 38 | if (err) { 39 | console.log(err); 40 | return reject(err); 41 | } 42 | connection.query(sql, [user_id, referral_code], function (err, results) { 43 | connection.release(); 44 | if (err) { 45 | console.log(err); 46 | return reject(err); 47 | } 48 | return resolve(results.insertId); 49 | }); 50 | }); 51 | }) 52 | }; 53 | 54 | // export const updateMarketData = async function(capID, data) { 55 | // return new Promise((resolve, reject) => { 56 | // var sql = "UPDATE crypto_market_data SET price_usd = ?, last_updated = ? WHERE id = ?"; 57 | // pool.getConnection(function(err, connection) { 58 | // if (err) { 59 | // console.log(err); 60 | // return reject(err); 61 | // } 62 | // var date = new Date(data.last_updated); 63 | // connection.query(sql, [data.quote.USD.price, format(date, 'YYYY-MM-DD HH:mm:ss'), capID], function(err, results) { 64 | // connection.release(); 65 | // if (err) { 66 | // console.log(err); 67 | // return reject(err); 68 | // } 69 | // return resolve(results); 70 | // }); 71 | // }); 72 | // }) 73 | // }; 74 | 75 | export const updateMarketData = async function (capID, data) { 76 | return new Promise((resolve, reject) => { 77 | var sql = "UPDATE crypto_market_data SET price_usd = ?, last_updated = ? WHERE id = ?"; 78 | var date = new Date(data.last_updated); 79 | pool.query(sql, [data.quote.USD.price, format(date, 'YYYY-MM-DD HH:mm:ss'), capID], function (err, results) { 80 | if (err) { 81 | console.log(err); 82 | return reject(err); 83 | } 84 | return resolve(results); 85 | }); 86 | }) 87 | }; 88 | 89 | export const loginUser = async function (email) { 90 | return new Promise((resolve, reject) => { 91 | var sql = "SELECT * FROM users WHERE email = ? AND status = 0"; 92 | pool.getConnection(function (err, connection) { 93 | if (err) { 94 | console.log(err); 95 | return reject(err); 96 | } 97 | connection.query(sql, [email], function (err, results) { 98 | connection.release(); 99 | if (err) { 100 | console.log(err); 101 | return reject(err); 102 | } 103 | return resolve(results[0]); 104 | }); 105 | }); 106 | }) 107 | }; 108 | 109 | export const getAllMarketData = async function () { 110 | return new Promise((resolve, reject) => { 111 | var sql = "SELECT * FROM crypto_market_data"; 112 | pool.getConnection(function (err, connection) { 113 | if (err) { 114 | console.log(err); 115 | return reject(err); 116 | } 117 | 118 | connection.query(sql, [], function (err, results) { 119 | connection.release(); 120 | if (err) { 121 | console.log(err); 122 | return reject(err); 123 | } 124 | return resolve(results); 125 | }); 126 | }); 127 | }) 128 | }; 129 | -------------------------------------------------------------------------------- /models/market_data.js: -------------------------------------------------------------------------------- 1 | import mysql from 'mysql'; 2 | import { format } from 'fecha'; 3 | 4 | var pool = mysql.createPool({ 5 | connectionLimit: 100, 6 | host: 'localhost', 7 | user: 'alek_db', 8 | password: '(&zjn$#2Z', 9 | database: 'tickets', 10 | timezone: 'Z' 11 | }); 12 | 13 | export const getMarketData = async function (capID) { 14 | return new Promise((resolve, reject) => { 15 | var sql = "SELECT * FROM crypto_market_data WHERE id = ?"; 16 | pool.getConnection(function (err, connection) { 17 | if (err) { 18 | console.log(err); 19 | return reject(err); 20 | } 21 | connection.on('error', function (err) { 22 | console.log('>>> error >>>', err); 23 | }); 24 | 25 | connection.query(sql, [capID], function (err, results) { 26 | connection.release(); 27 | if (err) { 28 | console.log(err); 29 | return reject(err); 30 | } 31 | return resolve(results[0]); 32 | }); 33 | }); 34 | }) 35 | }; 36 | 37 | export const insertMarketData = async function (data) { 38 | return new Promise((resolve, reject) => { 39 | var sql = "INSERT INTO crypto_market_data (id, name, symbol, price_usd, last_updated) VALUES (?, ?, ?, ?, ?)"; 40 | pool.getConnection(function (err, connection) { 41 | if (err) { 42 | console.log(err); 43 | return reject(err); 44 | } 45 | var date = new Date(data.last_updated); 46 | connection.query(sql, [data.id, data.name, data.symbol, data.quote.USD.price, format(date, 'YYYY-MM-DD HH:mm:ss')], function (err, results) { 47 | connection.release(); 48 | if (err) { 49 | console.log(err); 50 | return reject(err); 51 | } 52 | return resolve(results.insertId); 53 | }); 54 | }); 55 | }) 56 | }; 57 | 58 | // export const updateMarketData = async function(capID, data) { 59 | // return new Promise((resolve, reject) => { 60 | // var sql = "UPDATE crypto_market_data SET price_usd = ?, last_updated = ? WHERE id = ?"; 61 | // pool.getConnection(function(err, connection) { 62 | // if (err) { 63 | // console.log(err); 64 | // return reject(err); 65 | // } 66 | // var date = new Date(data.last_updated); 67 | // connection.query(sql, [data.quote.USD.price, format(date, 'YYYY-MM-DD HH:mm:ss'), capID], function(err, results) { 68 | // connection.release(); 69 | // if (err) { 70 | // console.log(err); 71 | // return reject(err); 72 | // } 73 | // return resolve(results); 74 | // }); 75 | // }); 76 | // }) 77 | // }; 78 | 79 | export const updateMarketData = async function (capID, data) { 80 | return new Promise((resolve, reject) => { 81 | var sql = "UPDATE crypto_market_data SET price_usd = ?, last_updated = ? WHERE id = ?"; 82 | var date = new Date(data.last_updated); 83 | pool.query(sql, [data.quote.USD.price, format(date, 'YYYY-MM-DD HH:mm:ss'), capID], function (err, results) { 84 | if (err) { 85 | console.log(err); 86 | return reject(err); 87 | } 88 | return resolve(results); 89 | }); 90 | }) 91 | }; 92 | 93 | export const loginUser = async function (email) { 94 | return new Promise((resolve, reject) => { 95 | var sql = "SELECT * FROM users WHERE email = ? AND status = 0"; 96 | pool.getConnection(function (err, connection) { 97 | if (err) { 98 | console.log(err); 99 | return reject(err); 100 | } 101 | connection.query(sql, [email], function (err, results) { 102 | connection.release(); 103 | if (err) { 104 | console.log(err); 105 | return reject(err); 106 | } 107 | return resolve(results[0]); 108 | }); 109 | }); 110 | }) 111 | }; 112 | 113 | export const getAllMarketData = async function () { 114 | return new Promise((resolve, reject) => { 115 | var sql = "SELECT * FROM crypto_market_data"; 116 | pool.getConnection(function (err, connection) { 117 | if (err) { 118 | console.log(err); 119 | return reject(err); 120 | } 121 | 122 | connection.query(sql, [], function (err, results) { 123 | connection.release(); 124 | if (err) { 125 | console.log(err); 126 | return reject(err); 127 | } 128 | return resolve(results); 129 | }); 130 | }); 131 | }) 132 | }; 133 | -------------------------------------------------------------------------------- /routes/payments.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import * as path from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | import Stripe from 'stripe'; 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = path.dirname(__filename); 7 | import dotenv from 'dotenv'; 8 | dotenv.config({ path: path.resolve(__dirname, '../.env') }); 9 | import { mintNFTwithMetadata } from '../utils/tatum.js'; 10 | import { getTicketSummary, insertTicketSummary, updateTicketSummary, insertSoldTicket, updateSoldTicketNFT } from '../models/ticket.js'; 11 | import { getAllMarketData } from '../models/market_data.js'; 12 | 13 | var payment_router = express.Router(); 14 | const saltRounds = 10 15 | const startTicketPrice = 7.0; 16 | 17 | const stripe = Stripe(process.env.STRIPE_SECRET_KEY); 18 | const IPFS_URL = process.env.IPFS_URL; 19 | 20 | payment_router.post('/payStripe', async (req, res) => { 21 | try { 22 | console.log('Confirm pay', req.body); 23 | // Create the PaymentIntent 24 | let intent = await stripe.paymentIntents.create({ 25 | payment_method: req.body.payment_method_id, 26 | description: "Buy tickets", 27 | amount: Math.ceil(req.body.amount.toFixed(2) * 100), 28 | currency: 'usd', 29 | confirmation_method: 'manual', 30 | confirm: true 31 | }); 32 | // Send the response to the client 33 | return generateResponse(res, intent, req.body.ticket_count, req.body.userId, req.body.payment_method_id); 34 | } catch (e) { 35 | // Display error on client 36 | return res 37 | .status(400) 38 | .json({ error: e.message }); 39 | } 40 | }); 41 | 42 | const generateResponse = async (res, intent, ticket_count, userId, stripe_trans) => { 43 | if (intent.status === 'succeeded') { 44 | // The payment didn’t need any additional actions and completed! 45 | // Handle post-payment fulfillment 46 | var ticket_summary = await getTicketSummary(); 47 | 48 | for(var i = 0 ; i < ticket_count ; i++) { 49 | var ticket_id = await insertSoldTicket(userId, stripe_trans, ticket_summary.current_price, '', '', '', ticket_summary.current_price, '',''); 50 | var txId = null; 51 | try { 52 | const mintResp = await mintNFTwithMetadata(ticket_id, IPFS_URL); 53 | txId = mintResp.txId; 54 | } catch (err) { 55 | // Display error on client 56 | return res 57 | .status(err.response.data.statusCode) 58 | .json({ error: err.response.data }); 59 | } 60 | 61 | if (txId) { 62 | await updateSoldTicketNFT(userId, ticket_id, txId, ticket_id); 63 | } 64 | } 65 | 66 | ticket_summary.total_sold += ticket_count; 67 | const multiply = Math.floor(ticket_summary.total_sold / 500); 68 | var new_ticket_price = startTicketPrice + multiply * 0.01; 69 | await updateTicketSummary(ticket_summary.total_sold, new_ticket_price); 70 | 71 | return res 72 | .status(200) 73 | .json({ result: "success", new_ticket_price: new_ticket_price }); 74 | } else { 75 | // Invalid status 76 | return res 77 | .status(500) 78 | .json({ error: "Invalid PaymentIntent status" }); 79 | } 80 | }; 81 | 82 | 83 | payment_router.post('/payCrypto', async (req, res) => { 84 | try { 85 | console.log('Confirm crypto Pay', req.body); 86 | 87 | var ticket_summary = await getTicketSummary(); 88 | var ticket_count = req.body.ticketCount; 89 | var userId = req.body.userId; 90 | 91 | for(var i = 0 ; i < req.body.ticketCount ; i++) { 92 | var ticket_id = await insertSoldTicket(req.body.userId, '', 0, req.body.token, req.body.amount, req.body.txHash, req.body.usd_amount, '',''); 93 | var txId = null; 94 | try { 95 | txId = await mintNFTwithMetadata(ticket_id, IPFS_URL); 96 | } catch (err) { 97 | // Display error on client 98 | return res 99 | .status(err.response.data.statusCode) 100 | .json({ error: err.response.data }); 101 | } 102 | console.log(' >>> txId >>> ', txId); 103 | if (txId != null) { 104 | await updateSoldTicketNFT(userId, ticket_id, txId, ticket_id); 105 | } 106 | } 107 | 108 | ticket_summary.total_sold += ticket_count; 109 | const multiply = Math.floor(ticket_summary.total_sold / 500); 110 | var new_ticket_price = startTicketPrice + multiply * 0.01; 111 | await updateTicketSummary(ticket_summary.total_sold, new_ticket_price); 112 | 113 | return res 114 | .status(200) 115 | .json({ result: "success", new_ticket_price: new_ticket_price }); 116 | } catch (e) { 117 | // Display error on client 118 | return res 119 | .status(400) 120 | .json({ error: e.message }); 121 | } 122 | }); 123 | 124 | payment_router.get('/market_data', async (req, res) => { 125 | try { 126 | console.log('Get Market Data'); 127 | 128 | var market_data = await getAllMarketData(); 129 | return res 130 | .status(200) 131 | .json({ result: "success", market_data: market_data }); 132 | } catch (e) { 133 | // Display error on client 134 | return res 135 | .status(400) 136 | .json({ error: e.message }); 137 | } 138 | }); 139 | 140 | export default payment_router; 141 | -------------------------------------------------------------------------------- /models/ticket.js: -------------------------------------------------------------------------------- 1 | import mysql from 'mysql'; 2 | var pool = mysql.createPool({ 3 | host: 'localhost', 4 | user: 'alek_db', 5 | password: '(&zjn$#2Z', 6 | database: 'tickets' 7 | }); 8 | 9 | export const getTicketSummary = async function () { 10 | return new Promise((resolve, reject) => { 11 | var sql = "SELECT * FROM tickets_summary"; 12 | pool.getConnection(function (err, connection) { 13 | if (err) { 14 | console.log(err); 15 | return reject(err); 16 | } 17 | connection.query(sql, [], function (err, results) { 18 | connection.release(); 19 | if (err) { 20 | console.log(err); 21 | return reject(err); 22 | } 23 | return resolve(results[0]); 24 | }); 25 | }); 26 | }) 27 | }; 28 | 29 | export const insertTicketSummary = async function (tickets_sold, current_price) { 30 | return new Promise((resolve, reject) => { 31 | var sql = "INSERT INTO tickets_summary (total_sold, current_price) VALUES (?, ?)"; 32 | pool.getConnection(function (err, connection) { 33 | if (err) { 34 | console.log(err); 35 | return reject(err); 36 | } 37 | connection.query(sql, [tickets_sold, current_price], function (err, results) { 38 | connection.release(); 39 | if (err) { 40 | console.log(err); 41 | return reject(err); 42 | } 43 | console.log('Tickets summary data is inserted'); 44 | return resolve(results.insertId); 45 | }); 46 | }); 47 | }) 48 | }; 49 | 50 | export const updateTicketSummary = async function (tickets_sold, current_price) { 51 | return new Promise((resolve, reject) => { 52 | var sql = "UPDATE tickets_summary SET total_sold = ?, current_price = ?"; 53 | pool.getConnection(function (err, connection) { 54 | if (err) { 55 | console.log(err); 56 | return reject(err); 57 | } 58 | connection.query(sql, [tickets_sold, current_price], function (err, results) { 59 | connection.release(); 60 | if (err) { 61 | console.log(err); 62 | return reject(err); 63 | } 64 | return resolve(results); 65 | }); 66 | }); 67 | }) 68 | }; 69 | 70 | export const insertSoldTicket = async function (userId, stripe_trans, stripe_amount, crypto_token, crypto_amount, crypto_tx, usd_amount, nft_tx, nft_id) { 71 | return new Promise((resolve, reject) => { 72 | var sql = "INSERT INTO tickets_sold (user_id, stripe_trans, stripe_amount, crypto_token, \ 73 | crypto_amount, crypto_tx, usd_amount, nft_tx, nft_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; 74 | pool.getConnection(function (err, connection) { 75 | if (err) { 76 | console.log(err); 77 | return reject(err); 78 | } 79 | connection.query(sql, [userId, stripe_trans, stripe_amount, crypto_token, crypto_amount, crypto_tx, usd_amount, nft_tx, nft_id], function (err, results) { 80 | connection.release(); 81 | if (err) { 82 | console.log(err); 83 | return reject(err); 84 | } 85 | return resolve(results.insertId); 86 | }); 87 | }); 88 | }) 89 | } 90 | 91 | export const updateSoldTicketNFT = async function (userId, ticket_id, nft_tx, nft_id) { 92 | return new Promise((resolve, reject) => { 93 | var sql = "UPDATE tickets_sold SET nft_tx = ?, nft_id = ? WHERE user_id = ? AND ticket_id = ?"; 94 | pool.getConnection(function (err, connection) { 95 | if (err) { 96 | console.log(err); 97 | return reject(err); 98 | } 99 | connection.query(sql, [nft_tx, nft_id, userId, ticket_id], function (err, results) { 100 | connection.release(); 101 | if (err) { 102 | console.log(err); 103 | return reject(err); 104 | } 105 | return resolve(results.insertId); 106 | }); 107 | }); 108 | }) 109 | } 110 | 111 | export const getAllTickets = async function () { 112 | return new Promise((resolve, reject) => { 113 | var sql = "SELECT A.ticket_id, A.stripe_trans, A.stripe_amount, A.crypto_token, round(A.crypto_amount, 6) as crypto_amount, A.crypto_tx, case when LENGTH(A.crypto_tx) > 5 then CONCAT(left(A.crypto_tx, 5), '...', right(A.crypto_tx, 5)) else A.crypto_tx end AS short_crypto_tx, A.usd_amount, A.nft_id, B.email FROM tickets_sold AS A LEFT JOIN users AS B ON A.user_id = B.id"; 114 | pool.getConnection(function (err, connection) { 115 | if (err) { 116 | console.log(err); 117 | return reject(err); 118 | } 119 | connection.query(sql, [], function (err, results) { 120 | connection.release(); 121 | if (err) { 122 | console.log(err); 123 | return reject(err); 124 | } 125 | return resolve(results); 126 | }); 127 | }); 128 | }) 129 | }; 130 | 131 | 132 | export const getUserTickets = async function (userId) { 133 | return new Promise((resolve, reject) => { 134 | var sql = "SELECT ticket_id, stripe_trans, stripe_amount, crypto_token, round(crypto_amount, 6) as crypto_amount, crypto_tx, case when LENGTH(crypto_tx) > 5 then CONCAT(left(crypto_tx, 7), '...', right(crypto_tx, 7)) else crypto_tx end AS short_crypto_tx, usd_amount, nft_id FROM tickets_sold WHERE user_id = ?"; 135 | pool.getConnection(function (err, connection) { 136 | if (err) { 137 | console.log(err); 138 | return reject(err); 139 | } 140 | connection.query(sql, [userId], function (err, results) { 141 | connection.release(); 142 | if (err) { 143 | console.log(err); 144 | return reject(err); 145 | } 146 | return resolve(results); 147 | }); 148 | }); 149 | }) 150 | }; -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | import mysql from 'mysql'; 2 | var pool = mysql.createPool({ 3 | host: 'localhost', 4 | user: 'alek_db', 5 | password: '(&zjn$#2Z', 6 | database: 'tickets' 7 | }); 8 | 9 | export const createUser = async function(email, pwd, code, referral_code) { 10 | return new Promise((resolve, reject) => { 11 | var sql = "INSERT INTO users (email, pwd, auth_code, referral_code) VALUES (?, ?, ?, ?)"; 12 | pool.getConnection(function(err, connection) { 13 | if (err) { 14 | console.log(err); 15 | return reject(err); 16 | } 17 | connection.query(sql, [email, pwd, code, referral_code], function(err, results) { 18 | connection.release(); 19 | if (err) { 20 | console.log(err); 21 | return reject(err); 22 | } 23 | console.log('User is inserted'); 24 | return resolve(results.insertId); 25 | }); 26 | }); 27 | }) 28 | }; 29 | 30 | export const checkUser = async function(email) { 31 | return new Promise((resolve, reject) => { 32 | var sql = "SELECT * FROM users WHERE email = ?"; 33 | pool.getConnection(function(err, connection) { 34 | if (err) { 35 | console.log(err); 36 | return reject(err); 37 | } 38 | connection.query(sql, [email], function(err, results) { 39 | connection.release(); 40 | if (err) { 41 | console.log(err); 42 | return reject(err); 43 | } 44 | return resolve(results[0]); 45 | }); 46 | }); 47 | }) 48 | }; 49 | 50 | export const loginUser = async function(email) { 51 | return new Promise((resolve, reject) => { 52 | var sql = "SELECT * FROM users WHERE email = ? AND status = 0"; 53 | pool.getConnection(function(err, connection) { 54 | if (err) { 55 | console.log(err); 56 | return reject(err); 57 | } 58 | connection.query(sql, [email], function(err, results) { 59 | connection.release(); 60 | if (err) { 61 | console.log(err); 62 | return reject(err); 63 | } 64 | return resolve(results[0]); 65 | }); 66 | }); 67 | }) 68 | }; 69 | 70 | export const confirmUser = async function(email, code) { 71 | return new Promise((resolve, reject) => { 72 | var sql = "SELECT id, auth_code FROM users WHERE email = ?"; 73 | pool.getConnection(function(err, connection) { 74 | if (err) { 75 | console.log(err); 76 | return reject(err); 77 | } 78 | connection.query(sql, [email], function(err, results) { 79 | if (err) { 80 | connection.release(); 81 | console.log(err); 82 | return reject(err); 83 | } 84 | if (results[0] != undefined && results[0].auth_code == code) { 85 | // Confirm ok 86 | var userId = results[0].id; 87 | 88 | var sql = "UPDATE users SET status = 0 WHERE email = ?"; 89 | connection.query(sql, [email], function(err, results) { 90 | connection.release(); 91 | if (err) { 92 | console.log(err); 93 | return reject(err); 94 | } 95 | return resolve(userId); 96 | }); 97 | } else { 98 | return resolve(-1); 99 | } 100 | }); 101 | }); 102 | }) 103 | }; 104 | 105 | export const updateCode = async function(email, newCode) { 106 | return new Promise((resolve, reject) => { 107 | var sql = "UPDATE users SET auth_code = ? WHERE email = ?"; 108 | pool.getConnection(function(err, connection) { 109 | if (err) { 110 | console.log(err); 111 | return reject(err); 112 | } 113 | connection.query(sql, [newCode, email], function(err, results) { 114 | connection.release(); 115 | if (err) { 116 | console.log(err); 117 | return reject(err); 118 | } 119 | return resolve(results); 120 | }); 121 | }); 122 | }) 123 | }; 124 | 125 | export const getUserCode = async function(email) { 126 | return new Promise((resolve, reject) => { 127 | var sql = "SELECT auth_code FROM users WHERE email = ?"; 128 | pool.getConnection(function(err, connection) { 129 | if (err) { 130 | console.log(err); 131 | return reject(err); 132 | } 133 | connection.query(sql, [email], function(err, results) { 134 | connection.release(); 135 | if (err) { 136 | console.log(err); 137 | return reject(err); 138 | } 139 | 140 | return resolve(results[0].auth_code); 141 | }); 142 | }); 143 | }) 144 | }; 145 | 146 | export const updatePassword = async function(email, newPassword) { 147 | return new Promise((resolve, reject) => { 148 | var sql = "UPDATE users SET pwd = ? WHERE email = ?"; 149 | pool.getConnection(function(err, connection) { 150 | if (err) { 151 | console.log(err); 152 | return reject(err); 153 | } 154 | connection.query(sql, [newPassword, email], function(err, results) { 155 | connection.release(); 156 | if (err) { 157 | console.log(err); 158 | return reject(err); 159 | } 160 | return resolve(results); 161 | }); 162 | }); 163 | }) 164 | }; 165 | 166 | export const getPovList = async function(userId) { 167 | return new Promise((resolve, reject) => { 168 | var sql = "SELECT * FROM povlist WHERE user_id = ?"; 169 | pool.getConnection(function(err, connection) { 170 | if (err) { 171 | console.log(err); 172 | return reject(err); 173 | } 174 | connection.query(sql, [userId], function(err, results) { 175 | connection.release(); 176 | if (err) { 177 | console.log(err); 178 | return reject(err); 179 | } 180 | return resolve(results[0]); 181 | }); 182 | }); 183 | }) 184 | }; 185 | 186 | export const insertPovList = async function(userId, fb, ig, tt, tw) { 187 | return new Promise((resolve, reject) => { 188 | var sql = "INSERT INTO povlist (user_id, fb, ig, tt, tw) VALUES (?, ?, ?, ?, ?)"; 189 | pool.getConnection(function(err, connection) { 190 | if (err) { 191 | console.log(err); 192 | return reject(err); 193 | } 194 | connection.query(sql, [userId, fb, ig, tt, tw], function(err, results) { 195 | connection.release(); 196 | if (err) { 197 | console.log(err); 198 | return reject(err); 199 | } 200 | return resolve(results[0]); 201 | }); 202 | }); 203 | }) 204 | }; 205 | 206 | export const updatePovList = async function(userId, fb, ig, tt, tw) { 207 | return new Promise((resolve, reject) => { 208 | var sql = "UPDATE povlist SET fb = ?, ig = ?, tt = ?, tw = ? WHERE user_id = ?"; 209 | pool.getConnection(function(err, connection) { 210 | if (err) { 211 | console.log(err); 212 | return reject(err); 213 | } 214 | connection.query(sql, [fb, ig, tt, tw, userId], function(err, results) { 215 | connection.release(); 216 | if (err) { 217 | console.log(err); 218 | return reject(err); 219 | } 220 | return resolve(results); 221 | }); 222 | }); 223 | }) 224 | }; -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { getTicketSummary } from '../models/ticket.js'; 3 | import * as path from 'path'; 4 | import { fileURLToPath } from 'url'; 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = path.dirname(__filename); 7 | import dotenv from 'dotenv'; 8 | dotenv.config({ path: path.resolve(__dirname, '../.env') }); 9 | import voucher_codes from 'voucher-code-generator'; 10 | import nodemailer from 'nodemailer'; 11 | import jwt from 'jsonwebtoken'; 12 | import bcrypt from 'bcrypt'; 13 | import { insertReferralCode } from '../models/referrals.js'; 14 | 15 | import { 16 | createUser, 17 | loginUser, 18 | confirmUser, 19 | checkUser, 20 | updateCode, 21 | getUserCode, 22 | updatePassword, 23 | getPovList, 24 | insertPovList, 25 | updatePovList 26 | } 27 | from '../models/user.js'; 28 | 29 | import sgMail from '@sendgrid/mail'; 30 | sgMail.setApiKey(process.env.SENDGRID_API_KEY); 31 | 32 | const SENDGRID_SENDER = process.env.SENDGRID_SENDER; 33 | 34 | var user_router = express.Router(); 35 | const saltRounds = 10 36 | 37 | let transporter = nodemailer.createTransport({ 38 | host: 'smtp.mandrillapp.com', 39 | //host: 'smtp.sendgrid.net', 40 | port: 587, 41 | auth: { 42 | user: "apikey", 43 | pass: "md-I8uJg9iCZB8oqPXE0M5uhQ" 44 | //pass: process.env.SENDGRID_API_KEY 45 | } 46 | }); 47 | 48 | user_router.post('/', async (req, res) => { 49 | try { 50 | console.log('Create User', req.body); 51 | 52 | var email = req.body.email; 53 | var pwd = req.body.password; 54 | var referral_code = req.body.referral_code; 55 | 56 | if (referral_code == undefined) 57 | referral_code = ""; 58 | 59 | var user = await checkUser(email); 60 | console.log(user); 61 | if (user != undefined) { 62 | return res 63 | .status(409) 64 | .json({ error: "The email already exists" }); 65 | } 66 | let reg_code = voucher_codes.generate({ 67 | length: 6, 68 | count: 1, 69 | charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 70 | }); 71 | 72 | var salt = await bcrypt.genSalt(saltRounds); 73 | var hashPassword = await bcrypt.hash(pwd, salt); 74 | 75 | var insertedUser = await createUser(email, hashPassword, reg_code, referral_code); 76 | 77 | const msg = { 78 | to: email, 79 | from: SENDGRID_SENDER, 80 | subject: 'Verify your email', 81 | html: '
Welcome to the Haven Wood
Your verification code is ' + reg_code + '
', 82 | } 83 | 84 | transporter.sendMail(msg, function (err, info) { 85 | if (err) { 86 | console.log(err); 87 | return res 88 | .status(500) 89 | .json({ error: "Sending email is failed" }); 90 | } else { 91 | return res 92 | .status(200) 93 | .json({ result: "success" }); 94 | } 95 | }); 96 | /* 97 | sgMail 98 | .send(msg) 99 | .then((response) => { 100 | return res 101 | .status(200) 102 | .json({result: "success"}); 103 | }) 104 | .catch((error) => { 105 | console.log(error); 106 | return res 107 | .status(500) 108 | .json({error: "Sending email is failed"}); 109 | }) 110 | */ 111 | } catch (e) { 112 | console.log(e); 113 | return res 114 | .status(500) 115 | .json({ error: "500 Internal Server Error", message: null }); 116 | } 117 | }); 118 | 119 | user_router.post('/login', async (req, res) => { 120 | try { 121 | console.log('Login User', req.body); 122 | 123 | var email = req.body.email; 124 | var pwd = req.body.password; 125 | 126 | var user = await loginUser(email); 127 | 128 | if (user != null && user != undefined) { 129 | 130 | bcrypt 131 | .compare(pwd, user.pwd) 132 | .then(async (resp) => { 133 | if (resp) { 134 | let jwtSecretKey = process.env.JWT_SECRET_KEY; 135 | let data = { 136 | time: Date(), 137 | userId: user.id, 138 | } 139 | const token = jwt.sign(data, jwtSecretKey); 140 | 141 | var ticket_summary = await getTicketSummary(); 142 | 143 | return res 144 | .status(200) 145 | .json({ 146 | result: "success", 147 | token: token, 148 | ticket_price: ticket_summary.current_price, 149 | userId: user.id 150 | }); 151 | } else { 152 | console.debug("Password is wrong"); 153 | return res 154 | .status(401) 155 | .json({ error: "Incorrect email or password" }); 156 | } 157 | }) 158 | .catch(err => { 159 | console.error(err.message); 160 | }) 161 | } else { 162 | console.debug("No such user"); 163 | return res 164 | .status(401) 165 | .json({ error: "Incorrect email or password" }); 166 | } 167 | } catch (e) { 168 | console.log(e); 169 | return res 170 | .status(500) 171 | .json({ error: "500 Internal Server Error", message: null }); 172 | } 173 | }); 174 | 175 | user_router.post('/confirm', async (req, res) => { 176 | try { 177 | console.log('Confirm User', req.body); 178 | 179 | var email = req.body.email; 180 | var code = req.body.code; 181 | 182 | var userId = await confirmUser(email, code); 183 | if (userId != undefined && userId != -1) { 184 | let jwtSecretKey = process.env.JWT_SECRET_KEY; 185 | let data = { 186 | time: Date(), 187 | userId: userId, 188 | } 189 | const token = jwt.sign(data, jwtSecretKey); 190 | return res 191 | .status(200) 192 | .json({ result: "success", token: token, userId: userId }); 193 | } 194 | 195 | return res 196 | .status(401) 197 | .json({ error: "Incorrect email or code" }); 198 | 199 | } catch (e) { 200 | console.log(e); 201 | return res 202 | .status(500) 203 | .json({ error: "500 Internal Server Error", message: null }); 204 | } 205 | }); 206 | 207 | user_router.post('/sendcode', async (req, res) => { 208 | try { 209 | console.log('Send code', req.body); 210 | 211 | var email = req.body.email; 212 | 213 | var user = await checkUser(email); 214 | if (user == undefined) { 215 | return res 216 | .status(404) 217 | .json({ error: "The email doesn't exist" }); 218 | } 219 | 220 | let reg_code = voucher_codes.generate({ 221 | length: 6, 222 | count: 1, 223 | charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 224 | }); 225 | 226 | var updateResult = await updateCode(email, reg_code); 227 | 228 | const msg = { 229 | to: email, 230 | from: SENDGRID_SENDER, 231 | subject: 'Password reset code', 232 | html: 'Your password reset code is ' + reg_code + '
', 233 | } 234 | 235 | transporter.sendMail(msg, function (err, info) { 236 | if (err) { 237 | console.log(err); 238 | return res 239 | .status(500) 240 | .json({ error: "Sending email is failed" }); 241 | } else { 242 | return res 243 | .status(200) 244 | .json({ result: "success" }); 245 | } 246 | }); 247 | } catch (e) { 248 | console.log(e); 249 | return res 250 | .status(500) 251 | .json({ error: "500 Internal Server Error", message: null }); 252 | } 253 | }); 254 | 255 | user_router.patch('/changepassword', async (req, res) => { 256 | try { 257 | console.log('Change password', req.body); 258 | 259 | var email = req.body.email; 260 | var pwd = req.body.password; 261 | var code = req.body.code; 262 | 263 | var user = await checkUser(email); 264 | if (user == undefined) { 265 | return res 266 | .status(404) 267 | .json({ error: "The email doesn't exist" }); 268 | } 269 | 270 | var dbCode = await getUserCode(email); 271 | if (dbCode != code) { 272 | return res 273 | .status(400) 274 | .json({ error: "Invalid code" }); 275 | } 276 | 277 | var salt = await bcrypt.genSalt(saltRounds); 278 | var hashPassword = await bcrypt.hash(pwd, salt); 279 | 280 | await updatePassword(email, hashPassword); 281 | 282 | return res 283 | .status(200) 284 | .json({ result: "success" }); 285 | /* 286 | sgMail 287 | .send(msg) 288 | .then((response) => { 289 | return res 290 | .status(200) 291 | .json({result: "success"}); 292 | }) 293 | .catch((error) => { 294 | console.log(error); 295 | return res 296 | .status(500) 297 | .json({error: "Sending email is failed"}); 298 | }) 299 | */ 300 | } catch (e) { 301 | console.log(e); 302 | return res 303 | .status(500) 304 | .json({ error: "500 Internal Server Error", message: null }); 305 | } 306 | }); 307 | 308 | user_router.post('/joinpov', async (req, res) => { 309 | try { 310 | console.log('Join Pov List', req.body); 311 | 312 | var userId = req.body.userId; 313 | var fb = req.body.fb == true ? 1 : 0; 314 | var ig = req.body.ig == true ? 1 : 0; 315 | var tt = req.body.tt == true ? 1 : 0; 316 | var tw = req.body.tw == true ? 1 : 0; 317 | 318 | var pov = await getPovList(userId); 319 | 320 | if (pov == undefined) { 321 | await insertPovList(userId, fb, ig, tt, tw); 322 | } else { 323 | await updatePovList(userId, fb, ig, tt, tw); 324 | } 325 | 326 | let referral_code = voucher_codes.generate({ 327 | length: 20, 328 | count: 1, 329 | charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz" 330 | }); 331 | 332 | await insertReferralCode(userId, referral_code); 333 | 334 | return res 335 | .status(200) 336 | .json({ 337 | result: "success", 338 | referral_code : referral_code 339 | }); 340 | } catch (e) { 341 | console.log(e); 342 | return res 343 | .status(500) 344 | .json({ error: e }); 345 | } 346 | }); 347 | 348 | user_router.get('/povlist/:userid', async (req, res) => { 349 | try { 350 | console.log('Get Pov List', req.params); 351 | 352 | var userId = req.params.userid; 353 | 354 | var pov = await getPovList(userId); 355 | 356 | if (pov == undefined) { 357 | return res 358 | .status(200) 359 | .json({ 360 | result: "success", 361 | pov: { 362 | fb : false, 363 | ig: false, 364 | tt: false, 365 | tw: false 366 | } 367 | }); 368 | } else { 369 | return res 370 | .status(200) 371 | .json({ 372 | result: "success", 373 | pov: { 374 | fb: pov.fb == 1 ? true : false, 375 | ig: pov.ig == 1 ? true : false, 376 | tt: pov.tt == 1 ? true : false, 377 | tw: pov.tw == 1 ? true : false, 378 | } 379 | }); 380 | } 381 | } catch (e) { 382 | console.log(e); 383 | return res 384 | .status(500) 385 | .json({ error: e }); 386 | } 387 | }); 388 | 389 | /* 390 | user_router.patch('/:id/:uid', async(req, res) => { 391 | try { 392 | console.log('update application', req.params, req.body); 393 | 394 | const shopifyRes = await fetch(req.body.shopURL + '/admin/oauth/access_scopes.json', { 395 | method: 'GET', 396 | headers: {'Content-Type': 'application/json', 'X-Shopify-Access-Token': req.body.shopifyAccessToken} 397 | }); 398 | 399 | if (!shopifyRes.ok) { 400 | return res 401 | .status(200) 402 | .json({error: "shopify error", message: "Failed to validate shopify"}); 403 | } 404 | var res_data = await shopifyRes.json(); 405 | if (res_data === null || res_data === undefined) { 406 | return res 407 | .status(200) 408 | .json({error: "shopify error", message: "Failed to validate shopify"}); 409 | } 410 | 411 | var access_scopes = []; 412 | res_data.access_scopes.forEach(scope => { 413 | access_scopes.push(scope.handle); 414 | }); 415 | 416 | if (access_scopes.length == 0) { 417 | return res 418 | .status(200) 419 | .json({error: "shopify error", message: "No valid scopes"}); 420 | } 421 | 422 | await Project.updateApplication(req.params.id, req.params.uid, JSON.stringify(access_scopes), req.body); 423 | 424 | return res 425 | .status(200) 426 | .json({error: null, message: ""}); 427 | } catch (e) { 428 | console.log(e); 429 | return res 430 | .status(500) 431 | .json({error: "500 Internal Server Error", message: null}); 432 | } 433 | });*/ 434 | 435 | 436 | export default user_router; 437 | --------------------------------------------------------------------------------