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