Initial commit
This commit is contained in:
commit
5f0eed3851
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
lib
|
||||||
|
bin
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es2020": true
|
||||||
|
},
|
||||||
|
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 11
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
// https://eslint.org/docs/rules/
|
||||||
|
"semi": ["error", "always"],
|
||||||
|
"quotes": ["error", "double"],
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
"no-unused-vars": ["error", { "args": "none" }],
|
||||||
|
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
node_modules/.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
lib/
|
|
@ -0,0 +1,106 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var app = require("../lib/app").default;
|
||||||
|
var debug = require("debug")("js:server");
|
||||||
|
var http = require("http");
|
||||||
|
var fs = require("fs");
|
||||||
|
var path = require("path");
|
||||||
|
const assert = require('assert').strict;
|
||||||
|
const { PORT } = require("../lib/config");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port = normalizePort(PORT);
|
||||||
|
app.set("port", port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var server = http.createServer(app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port, function() {
|
||||||
|
if (typeof port === "string") {
|
||||||
|
fs.chmodSync(port, 0o666)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
server.on("error", onError);
|
||||||
|
server.on("listening", onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort (val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
assert (val.startsWith("/tmp/"));
|
||||||
|
if (fs.existsSync(PORT)) {
|
||||||
|
fs.unlinkSync(PORT);
|
||||||
|
} else if (!fs.existsSync(path.dirname(PORT))) {
|
||||||
|
fs.mkdirSync(path.dirname(PORT), { mode: 0o755 });
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError (error) {
|
||||||
|
if (error.syscall !== "listen") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === "string"
|
||||||
|
? "Pipe " + port
|
||||||
|
: "Port " + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case "EACCES":
|
||||||
|
console.error(bind + " requires elevated privileges");
|
||||||
|
process.exit(1);
|
||||||
|
// eslint-disable-next-line no-unreachable
|
||||||
|
break;
|
||||||
|
case "EADDRINUSE":
|
||||||
|
console.error(bind + " is already in use");
|
||||||
|
process.exit(1);
|
||||||
|
// eslint-disable-next-line no-unreachable
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening () {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === "string"
|
||||||
|
? "pipe " + addr
|
||||||
|
: "port " + addr.port;
|
||||||
|
debug("Listening on " + bind);
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"name": "serum-rest",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "tsc && node ./bin/www",
|
||||||
|
"start-prof": "tsc && node --prof ./bin/www",
|
||||||
|
"build": "tsc",
|
||||||
|
"clean": "rm -r ./lib",
|
||||||
|
"lint-dry": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||||
|
"fix": "prettier --write . && eslint . --ext .js,.jsx,.ts,.tsx --fix",
|
||||||
|
"shell": "tsc && node -e \"$(< shell)\" -i --experimental-repl-await"
|
||||||
|
},
|
||||||
|
"repository": "git@github.com:project-serum/serum-rest-wip.git",
|
||||||
|
"author": "Nathaniel Parke <nathaniel.parke@gmail.com>",
|
||||||
|
"devDependencies": {
|
||||||
|
"@tsconfig/node12": "^1.0.7",
|
||||||
|
"@types/express": "^4.17.8",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.5.0",
|
||||||
|
"@typescript-eslint/parser": "^4.5.0",
|
||||||
|
"eslint": "^7.11.0",
|
||||||
|
"eslint-config-prettier": "^6.13.0",
|
||||||
|
"eslint-config-standard": "^14.1.1",
|
||||||
|
"eslint-plugin-import": "^2.22.1",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
|
"prettier": "^2.1.2",
|
||||||
|
"typescript": "^4.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@project-serum/serum": "^0.13.5",
|
||||||
|
"@solana/web3.js": "^0.81.0",
|
||||||
|
"bn.js": "^5.1.3",
|
||||||
|
"debug": "^4.2.0",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-async-handler": "^1.1.4",
|
||||||
|
"http-errors": "^1.8.0",
|
||||||
|
"morgan": "^1.10.0",
|
||||||
|
"winston": "^3.3.3",
|
||||||
|
"winston-daily-rotate-file": "^4.5.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
const lib = require('./lib/index');
|
||||||
|
const solana = require('@solana/web3.js');
|
||||||
|
const serum = require('@project-serum/serum');
|
|
@ -0,0 +1,50 @@
|
||||||
|
import createError from "http-errors";
|
||||||
|
import express from "express";
|
||||||
|
import { default as morgan } from "morgan";
|
||||||
|
import indexRouter from "./routes";
|
||||||
|
import { logger, morganStream } from "./utils";
|
||||||
|
import * as config from "./config";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
morgan("combined", {
|
||||||
|
stream: morganStream,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
|
||||||
|
app.use("/", indexRouter);
|
||||||
|
|
||||||
|
// catch 404 and forward to error handler
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
next(createError(404));
|
||||||
|
});
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
app.use((err, req, res, next) => {
|
||||||
|
logger.log(
|
||||||
|
"error",
|
||||||
|
`Express error handler called for error ${err.name}: \n ${err.stack}`
|
||||||
|
);
|
||||||
|
// set locals, only providing error in development
|
||||||
|
res.locals.message = err.message;
|
||||||
|
res.locals.error = req.app.get("env") === "dev" ? err : {};
|
||||||
|
|
||||||
|
// render the error page
|
||||||
|
res.status(err.status || 500);
|
||||||
|
res.send("error");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (config.RESTART_INTERVAL_SEC) {
|
||||||
|
const secs = config.RESTART_INTERVAL_SEC + Math.floor(Math.random() * 30);
|
||||||
|
setTimeout(() => {
|
||||||
|
logger.error(
|
||||||
|
`Restarting server on port ${config.PORT} after ${secs} seconds due to timer`
|
||||||
|
);
|
||||||
|
process.exit(0);
|
||||||
|
}, secs * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
|
@ -0,0 +1,16 @@
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
// use passed port if sepcified otherwise default to the .env file
|
||||||
|
const PASSED_PORT = process.env.PORT;
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
export const PORT = PASSED_PORT || process.env.PORT;
|
||||||
|
export const ENV = process.env.ENVIRONMENT;
|
||||||
|
export const SECRETS_FILE = process.env.SECRETS_FILE || "";
|
||||||
|
export const LOGGING_DIR = process.env.LOGGING_DIR || "";
|
||||||
|
|
||||||
|
// check truthiness of this to determine if we should restart at interval
|
||||||
|
export const RESTART_INTERVAL_SEC = parseInt(
|
||||||
|
process.env.RESTART_INTERVAL_SEC || "0"
|
||||||
|
);
|
|
@ -0,0 +1,4 @@
|
||||||
|
import * as utils from "./utils";
|
||||||
|
import * as configs from "./config";
|
||||||
|
|
||||||
|
export { utils, configs };
|
|
@ -0,0 +1,11 @@
|
||||||
|
import express from "express";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/", (req, res, next) => {
|
||||||
|
res.send(
|
||||||
|
"Hello from the Serum rest server!"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export { router as default };
|
|
@ -0,0 +1,98 @@
|
||||||
|
import { LOGGING_DIR, SECRETS_FILE } from "./config";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
import BN from "bn.js";
|
||||||
|
import winston, { format } from "winston";
|
||||||
|
import "winston-daily-rotate-file";
|
||||||
|
const { combine, timestamp, printf } = format;
|
||||||
|
import fs from "fs";
|
||||||
|
// Logging
|
||||||
|
if (
|
||||||
|
LOGGING_DIR &&
|
||||||
|
!fs.existsSync(LOGGING_DIR) &&
|
||||||
|
process.env.ENVIRONMENT === "prod"
|
||||||
|
) {
|
||||||
|
fs.mkdirSync(LOGGING_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
const logFormat = printf(({ level, message, timestamp }) => {
|
||||||
|
return `${timestamp} ${level}: ${message}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const logger = winston.createLogger({
|
||||||
|
level: "silly",
|
||||||
|
format: combine(timestamp(), logFormat),
|
||||||
|
transports: [
|
||||||
|
new winston.transports.Console({
|
||||||
|
format: winston.format.simple(),
|
||||||
|
level: "info",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (process.env.ENVIRONMENT === "prod") {
|
||||||
|
logger.add(
|
||||||
|
new winston.transports.DailyRotateFile({
|
||||||
|
dirname: LOGGING_DIR,
|
||||||
|
filename: "remote_js_server-ERROR-%DATE%.log",
|
||||||
|
datePattern: "YYYY-MM-DD-HH",
|
||||||
|
maxSize: "200m",
|
||||||
|
maxFiles: "1",
|
||||||
|
utc: true,
|
||||||
|
level: "error",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
logger.add(
|
||||||
|
new winston.transports.DailyRotateFile({
|
||||||
|
dirname: LOGGING_DIR,
|
||||||
|
filename: "remote_js_server-INFO-%DATE%.log",
|
||||||
|
datePattern: "YYYY-MM-DD-HH",
|
||||||
|
maxSize: "200m",
|
||||||
|
maxFiles: "3",
|
||||||
|
utc: true,
|
||||||
|
level: "info",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
logger.add(
|
||||||
|
new winston.transports.DailyRotateFile({
|
||||||
|
dirname: LOGGING_DIR,
|
||||||
|
filename: "remote_js_server-DEBUG-%DATE%.log",
|
||||||
|
datePattern: "YYYY-MM-DD-HH",
|
||||||
|
maxSize: "200m",
|
||||||
|
maxFiles: "3",
|
||||||
|
utc: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MorganStream {
|
||||||
|
write(text: string) {
|
||||||
|
logger.info(text.replace(/\n$/, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const morganStream = new MorganStream();
|
||||||
|
|
||||||
|
export const getKeys = (
|
||||||
|
keys: string[]
|
||||||
|
): string[] => {
|
||||||
|
const allSecrets = JSON.parse(readFileSync(SECRETS_FILE, "utf-8"));
|
||||||
|
const secrets: string[] = [];
|
||||||
|
for (const key of keys) {
|
||||||
|
secrets.push(allSecrets[key]);
|
||||||
|
}
|
||||||
|
return secrets;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUnixTs = (): number => {
|
||||||
|
return new Date().getTime() / 1000;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function sleep(time: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, time));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function divideBnToNumber(numerator: BN, denominator: BN): number {
|
||||||
|
const quotient = numerator.div(denominator).toNumber();
|
||||||
|
const rem = numerator.umod(denominator);
|
||||||
|
const gcd = rem.gcd(denominator);
|
||||||
|
return quotient + rem.div(gcd).toNumber() / denominator.div(gcd).toNumber();
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"extends": "@tsconfig/node12/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./lib",
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"module": "CommonJS"
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*"],
|
||||||
|
"exclude": ["./src/**/*.test.js", "node_modules", "**/node_modules"]
|
||||||
|
}
|
Loading…
Reference in New Issue