feat: complete day 7
This commit is contained in:
@@ -24,6 +24,8 @@ async function fetchShopifyCustomers() {
|
|||||||
const curlCommand = `curl -i -X GET "${nextUrl}"`;
|
const curlCommand = `curl -i -X GET "${nextUrl}"`;
|
||||||
const { stdout } = await execPromise(curlCommand);
|
const { stdout } = await execPromise(curlCommand);
|
||||||
|
|
||||||
|
console.log(stdout);
|
||||||
|
|
||||||
// Separate headers and body
|
// Separate headers and body
|
||||||
const parts = stdout.split("\r\n\r\n");
|
const parts = stdout.split("\r\n\r\n");
|
||||||
const rawHeaders = parts[0];
|
const rawHeaders = parts[0];
|
||||||
@@ -70,11 +72,13 @@ async function syncCustomers() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncCustomers();
|
||||||
|
|
||||||
// run every minute
|
// run every minute
|
||||||
cron.schedule("* * * * *", () => {
|
// cron.schedule("* * * * *", () => {
|
||||||
console.log("Running Shopify customer sync...");
|
// console.log("Running Shopify customer sync...");
|
||||||
syncCustomers();
|
// syncCustomers();
|
||||||
});
|
// });
|
||||||
|
|
||||||
// For manual testing
|
// For manual testing
|
||||||
module.exports = { syncCustomers };
|
module.exports = { syncCustomers };
|
||||||
|
|||||||
+17
-15
@@ -1,11 +1,12 @@
|
|||||||
var createError = require('http-errors');
|
var createError = require("http-errors");
|
||||||
var express = require('express');
|
var express = require("express");
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
var cookieParser = require('cookie-parser');
|
var cookieParser = require("cookie-parser");
|
||||||
var logger = require('morgan');
|
var logger = require("morgan");
|
||||||
|
|
||||||
var indexRouter = require('./routes/index');
|
var indexRouter = require("./routes/index");
|
||||||
var usersRouter = require('./routes/users');
|
var usersRouter = require("./routes/users");
|
||||||
|
const userApiRouter = require("./routes/user");
|
||||||
|
|
||||||
const db = require("./models");
|
const db = require("./models");
|
||||||
var cors = require("cors");
|
var cors = require("cors");
|
||||||
@@ -13,17 +14,18 @@ var cors = require("cors");
|
|||||||
var app = express();
|
var app = express();
|
||||||
app.set("db", db);
|
app.set("db", db);
|
||||||
// view engine setup
|
// view engine setup
|
||||||
app.set('views', path.join(__dirname, 'views'));
|
app.set("views", path.join(__dirname, "views"));
|
||||||
app.set('view engine', 'jade');
|
app.set("view engine", "jade");
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(logger('dev'));
|
app.use(logger("dev"));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.urlencoded({ extended: false }));
|
app.use(express.urlencoded({ extended: false }));
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.use(express.static(path.join(__dirname, 'public')));
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
|
||||||
app.use('/', indexRouter);
|
app.use("/", indexRouter);
|
||||||
app.use('/users', usersRouter);
|
app.use("/users", usersRouter);
|
||||||
|
app.use("/api/v1/user", userApiRouter);
|
||||||
|
|
||||||
// catch 404 and forward to error handler
|
// catch 404 and forward to error handler
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
@@ -34,11 +36,11 @@ app.use(function (req, res, next) {
|
|||||||
app.use(function (err, req, res, next) {
|
app.use(function (err, req, res, next) {
|
||||||
// set locals, only providing error in development
|
// set locals, only providing error in development
|
||||||
res.locals.message = err.message;
|
res.locals.message = err.message;
|
||||||
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
res.locals.error = req.app.get("env") === "development" ? err : {};
|
||||||
|
|
||||||
// render the error page
|
// render the error page
|
||||||
res.status(err.status || 500);
|
res.status(err.status || 500);
|
||||||
res.render('error');
|
res.render("error");
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
|||||||
+47
-33
@@ -1,4 +1,4 @@
|
|||||||
'use strict';
|
"use strict";
|
||||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
|
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
|
||||||
/**
|
/**
|
||||||
* Sequelize File
|
* Sequelize File
|
||||||
@@ -8,49 +8,63 @@
|
|||||||
* @author Ryan Wong
|
* @author Ryan Wong
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const fs = require('fs');
|
const fs = require("fs");
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
let Sequelize = require('sequelize');
|
let Sequelize = require("sequelize");
|
||||||
const basename = path.basename(__filename);
|
const basename = path.basename(__filename);
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require("sequelize");
|
||||||
const config = {
|
const config = {
|
||||||
DB_DATABASE: 'mysql',
|
DB_DATABASE: "mysql",
|
||||||
DB_USERNAME: 'root',
|
DB_USERNAME: "root",
|
||||||
DB_PASSWORD: 'root',
|
DB_PASSWORD: process.env.DB_PASSWORD || "root",
|
||||||
DB_ADAPTER: 'mysql',
|
DB_ADAPTER: "mysql",
|
||||||
DB_NAME: 'day_1',
|
DB_NAME: "day_7",
|
||||||
DB_HOSTNAME: 'localhost',
|
DB_HOSTNAME: "localhost",
|
||||||
DB_PORT: 3306,
|
DB_PORT: 3306,
|
||||||
};
|
};
|
||||||
|
|
||||||
let db = {};
|
let db = {};
|
||||||
|
|
||||||
let sequelize = new Sequelize(config.DB_DATABASE, config.DB_USERNAME, config.DB_PASSWORD, {
|
let sequelize = new Sequelize(
|
||||||
dialect: config.DB_ADAPTER,
|
config.DB_NAME,
|
||||||
username: config.DB_USERNAME,
|
config.DB_USERNAME,
|
||||||
password: config.DB_PASSWORD,
|
config.DB_PASSWORD,
|
||||||
database: config.DB_NAME,
|
{
|
||||||
host: config.DB_HOSTNAME,
|
dialect: config.DB_ADAPTER,
|
||||||
port: config.DB_PORT,
|
username: config.DB_USERNAME,
|
||||||
logging: console.log,
|
password: config.DB_PASSWORD,
|
||||||
timezone: '-04:00',
|
database: config.DB_NAME,
|
||||||
pool: {
|
host: config.DB_HOSTNAME,
|
||||||
maxConnections: 1,
|
port: config.DB_PORT,
|
||||||
minConnections: 0,
|
logging: console.log,
|
||||||
maxIdleTime: 100,
|
timezone: "-04:00",
|
||||||
},
|
pool: {
|
||||||
define: {
|
maxConnections: 1,
|
||||||
timestamps: false,
|
minConnections: 0,
|
||||||
underscoredAll: true,
|
maxIdleTime: 100,
|
||||||
underscored: true,
|
},
|
||||||
},
|
define: {
|
||||||
});
|
timestamps: false,
|
||||||
|
underscoredAll: true,
|
||||||
|
underscored: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// sequelize.sync({ force: true });
|
sequelize
|
||||||
|
.sync()
|
||||||
|
.then(() => {
|
||||||
|
console.log("All tables synced successfully.");
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Failed to sync tables:", err);
|
||||||
|
});
|
||||||
|
|
||||||
fs.readdirSync(__dirname)
|
fs.readdirSync(__dirname)
|
||||||
.filter((file) => {
|
.filter((file) => {
|
||||||
return file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js';
|
return (
|
||||||
|
file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.forEach((file) => {
|
.forEach((file) => {
|
||||||
var model = require(path.join(__dirname, file))(sequelize, DataTypes);
|
var model = require(path.join(__dirname, file))(sequelize, DataTypes);
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
const user = sequelize.define("user", {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
wallet_id: DataTypes.STRING,
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
};
|
||||||
+5
-2
@@ -3,7 +3,8 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./bin/www"
|
"start": "node ./bin/www",
|
||||||
|
"dev": "node --watch --env-file=.env ./bin/www"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie-parser": "~1.4.4",
|
"cookie-parser": "~1.4.4",
|
||||||
@@ -14,6 +15,8 @@
|
|||||||
"jade": "~1.11.0",
|
"jade": "~1.11.0",
|
||||||
"morgan": "~1.9.1",
|
"morgan": "~1.9.1",
|
||||||
"mysql2": "^2.3.3",
|
"mysql2": "^2.3.3",
|
||||||
"sequelize": "^6.15.1"
|
"node-input-validator": "^4.5.1",
|
||||||
|
"sequelize": "^6.15.1",
|
||||||
|
"web3": "^4.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const { user } = require("../models");
|
||||||
|
const {
|
||||||
|
validateInput,
|
||||||
|
handleValidationErrorForAPI,
|
||||||
|
} = require("../services/ValidationService");
|
||||||
|
const Web3Service = require("../services/Web3Service");
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/", async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const users = await user.findAll();
|
||||||
|
res.json({ success: true, data: users });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const createUserRules = {
|
||||||
|
name: "required|string",
|
||||||
|
};
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/",
|
||||||
|
validateInput(createUserRules),
|
||||||
|
handleValidationErrorForAPI,
|
||||||
|
async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
await user.create(req.body);
|
||||||
|
res.json({ success: true, message: "User created successfully" });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// POST /api/v1/user/wallet
|
||||||
|
router.post(
|
||||||
|
"/wallet",
|
||||||
|
validateInput({ id: "required|integer" }),
|
||||||
|
handleValidationErrorForAPI,
|
||||||
|
async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { id: userId } = req.body;
|
||||||
|
const u = await user.findByPk(userId);
|
||||||
|
|
||||||
|
if (!u) {
|
||||||
|
return res
|
||||||
|
.status(404)
|
||||||
|
.json({ success: false, error: "User not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const wallet = Web3Service.createWallet();
|
||||||
|
await u.update({ wallet_id: wallet.address });
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: { address: wallet.address, privateKey: wallet.privateKey },
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ success: false, error: err.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// GET /api/v1/user/sign?private_key=...&message=...
|
||||||
|
router.get("/sign", (req, res) => {
|
||||||
|
try {
|
||||||
|
const { private_key, message } = req.query;
|
||||||
|
const signed = Web3Service.signMessage(
|
||||||
|
private_key,
|
||||||
|
message || "default message"
|
||||||
|
);
|
||||||
|
res.json({ success: true, data: signed });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ success: false, error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// GET /api/v1/user/account?private_key=...
|
||||||
|
router.get("/account", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { private_key } = req.query;
|
||||||
|
const account =
|
||||||
|
Web3Service.web3.eth.accounts.privateKeyToAccount(private_key);
|
||||||
|
const balance = await Web3Service.getBalance(account.address);
|
||||||
|
res.json({ success: true, data: { address: account.address, balance } });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ success: false, error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// GET /api/v1/user/transfer?private_key=...&to_address=...&amount=...
|
||||||
|
router.get("/transfer", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { private_key, to_address, amount } = req.query;
|
||||||
|
const tx = await Web3Service.transfer(private_key, to_address, amount);
|
||||||
|
res.json({ success: true, data: tx });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ success: false, error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:id", async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const u = await user.findByPk(req.params.id);
|
||||||
|
if (!u) {
|
||||||
|
return res.status(404).json({ error: "User not found" });
|
||||||
|
}
|
||||||
|
res.json({ success: true, data: u });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateUserRules = {
|
||||||
|
name: "string",
|
||||||
|
wallet_id: "string",
|
||||||
|
};
|
||||||
|
|
||||||
|
router.put(
|
||||||
|
"/:id",
|
||||||
|
validateInput(updateUserRules),
|
||||||
|
handleValidationErrorForAPI,
|
||||||
|
async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const u = await user.findByPk(req.params.id);
|
||||||
|
if (!u) {
|
||||||
|
return res.status(404).json({ error: "User not found" });
|
||||||
|
}
|
||||||
|
await u.update(req.body);
|
||||||
|
res.json({ success: true, message: "User updated successfully" });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete("/:id", async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const u = await user.findByPk(req.params.id);
|
||||||
|
if (!u) {
|
||||||
|
return res.status(404).json({ error: "User not found" });
|
||||||
|
}
|
||||||
|
await u.destroy();
|
||||||
|
res.json({ success: true, message: "User deleted successfully" });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// services/Web3Service.js
|
||||||
|
const { Web3 } = require("web3");
|
||||||
|
|
||||||
|
const web3 = new Web3(process.env.ALCHEMY_API_URL);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
web3,
|
||||||
|
createWallet: () => {
|
||||||
|
return web3.eth.accounts.create();
|
||||||
|
},
|
||||||
|
signMessage: (privateKey, message) => {
|
||||||
|
return web3.eth.accounts.sign(message, privateKey);
|
||||||
|
},
|
||||||
|
getBalance: async (address) => {
|
||||||
|
const balanceWei = await web3.eth.getBalance(address);
|
||||||
|
return web3.utils.fromWei(balanceWei, "ether");
|
||||||
|
},
|
||||||
|
transfer: async (privateKey, to, amount) => {
|
||||||
|
const account = web3.eth.accounts.privateKeyToAccount(privateKey);
|
||||||
|
const from = account.address;
|
||||||
|
const nonce = await web3.eth.getTransactionCount(from, "latest");
|
||||||
|
|
||||||
|
const gasPrice = await web3.eth.getGasPrice();
|
||||||
|
|
||||||
|
const tx = {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
value: web3.utils.toWei(amount, "ether"),
|
||||||
|
gas: 21000,
|
||||||
|
gasPrice,
|
||||||
|
nonce,
|
||||||
|
};
|
||||||
|
|
||||||
|
const signed = await account.signTransaction(tx);
|
||||||
|
return web3.eth.sendSignedTransaction(signed.rawTransaction);
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user