mirror of
https://github.com/ragestudio/linebridge.git
synced 2025-06-09 10:34:17 +00:00
Merge from local
This commit is contained in:
parent
ff38d45b96
commit
11d8a862b4
@ -1,182 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
require("dotenv").config()
|
||||
require("sucrase/register")
|
||||
|
||||
const path = require("node:path")
|
||||
const Module = require("node:module")
|
||||
const { Buffer } = require("node:buffer")
|
||||
const { webcrypto: crypto } = require("node:crypto")
|
||||
const { InfisicalClient } = require("@infisical/sdk")
|
||||
const moduleAlias = require("module-alias")
|
||||
const { onExit } = require("signal-exit")
|
||||
|
||||
// Override file execution arg
|
||||
process.argv.splice(1, 1)
|
||||
process.argv[1] = path.resolve(process.argv[1])
|
||||
|
||||
// Expose boot function to global
|
||||
global.Boot = Boot
|
||||
global.isProduction = process.env.NODE_ENV === "production"
|
||||
|
||||
global["__root"] = path.resolve(process.cwd())
|
||||
global["__src"] = path.resolve(globalThis["__root"], path.dirname(process.argv[1]))
|
||||
|
||||
global["aliases"] = {
|
||||
"root": global["__root"],
|
||||
"src": global["__src"],
|
||||
|
||||
// expose shared resources
|
||||
"@db": path.resolve(process.cwd(), "db_models"),
|
||||
"@db_models": path.resolve(process.cwd(), "db_models"),
|
||||
"@shared-utils": path.resolve(process.cwd(), "utils"),
|
||||
"@shared-classes": path.resolve(process.cwd(), "classes"),
|
||||
"@shared-lib": path.resolve(process.cwd(), "lib"),
|
||||
"@shared-middlewares": path.resolve(process.cwd(), "middlewares"),
|
||||
|
||||
// expose internal resources
|
||||
"@lib": path.resolve(global["__src"], "lib"),
|
||||
"@middlewares": path.resolve(global["__src"], "middlewares"),
|
||||
"@controllers": path.resolve(global["__src"], "controllers"),
|
||||
"@config": path.resolve(global["__src"], "config"),
|
||||
"@shared": path.resolve(global["__src"], "shared"),
|
||||
"@classes": path.resolve(global["__src"], "classes"),
|
||||
"@models": path.resolve(global["__src"], "models"),
|
||||
"@services": path.resolve(global["__src"], "services"),
|
||||
"@utils": path.resolve(global["__src"], "utils"),
|
||||
}
|
||||
|
||||
function registerBaseAliases(fromPath, customAliases = {}) {
|
||||
if (typeof fromPath === "undefined") {
|
||||
if (module.parent.filename.includes("dist")) {
|
||||
fromPath = path.resolve(process.cwd(), "dist")
|
||||
} else {
|
||||
fromPath = path.resolve(process.cwd(), "src")
|
||||
}
|
||||
}
|
||||
|
||||
moduleAlias.addAliases({
|
||||
...customAliases,
|
||||
"@controllers": path.resolve(fromPath, "controllers"),
|
||||
"@middlewares": path.resolve(fromPath, "middlewares"),
|
||||
"@models": path.resolve(fromPath, "models"),
|
||||
"@classes": path.resolve(fromPath, "classes"),
|
||||
"@lib": path.resolve(fromPath, "lib"),
|
||||
"@utils": path.resolve(fromPath, "utils"),
|
||||
})
|
||||
}
|
||||
|
||||
function registerPatches() {
|
||||
global.b64Decode = (data) => {
|
||||
return Buffer.from(data, "base64").toString("utf-8")
|
||||
}
|
||||
global.b64Encode = (data) => {
|
||||
return Buffer.from(data, "utf-8").toString("base64")
|
||||
}
|
||||
|
||||
global.nanoid = (t = 21) => crypto.getRandomValues(new Uint8Array(t)).reduce(((t, e) => t += (e &= 63) < 36 ? e.toString(36) : e < 62 ? (e - 26).toString(36).toUpperCase() : e > 62 ? "-" : "_"), "");
|
||||
|
||||
Array.prototype.updateFromObjectKeys = function (obj) {
|
||||
this.forEach((value, index) => {
|
||||
if (obj[value] !== undefined) {
|
||||
this[index] = obj[value]
|
||||
}
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
global.ToBoolean = (value) => {
|
||||
if (typeof value === "boolean") {
|
||||
return value
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
return value.toLowerCase() === "true"
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function registerAliases() {
|
||||
registerBaseAliases(global["__src"], global["aliases"])
|
||||
}
|
||||
|
||||
async function injectEnvFromInfisical() {
|
||||
const envMode = global.FORCE_ENV ?? global.isProduction ? "prod" : "dev"
|
||||
|
||||
console.log(`[BOOT] 🔑 Injecting env variables from INFISICAL in [${envMode}] mode...`)
|
||||
|
||||
const client = new InfisicalClient({
|
||||
auth: {
|
||||
universalAuth: {
|
||||
clientId: process.env.INFISICAL_CLIENT_ID,
|
||||
clientSecret: process.env.INFISICAL_CLIENT_SECRET,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const secrets = await client.listSecrets({
|
||||
environment: envMode,
|
||||
path: process.env.INFISICAL_PATH ?? "/",
|
||||
projectId: process.env.INFISICAL_PROJECT_ID ?? null,
|
||||
includeImports: false,
|
||||
})
|
||||
|
||||
//inject to process.env
|
||||
secrets.forEach((secret) => {
|
||||
if (!(process.env[secret.secretKey])) {
|
||||
process.env[secret.secretKey] = secret.secretValue
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function Boot(main) {
|
||||
if (!main) {
|
||||
throw new Error("main class is not defined")
|
||||
}
|
||||
|
||||
console.log(`[BOOT] Booting in [${global.isProduction ? "production" : "development"}] mode...`)
|
||||
|
||||
if (process.env.INFISICAL_CLIENT_ID && process.env.INFISICAL_CLIENT_SECRET) {
|
||||
console.log(`[BOOT] INFISICAL Credentials found, injecting env variables from INFISICAL...`)
|
||||
await injectEnvFromInfisical()
|
||||
}
|
||||
|
||||
const instance = new main()
|
||||
|
||||
onExit((code, signal) => {
|
||||
console.log(`[BOOT] Cleaning up...`)
|
||||
|
||||
if (typeof instance.onClose === "function") {
|
||||
instance.onClose()
|
||||
}
|
||||
|
||||
instance.engine.close()
|
||||
}, {
|
||||
alwaysLast: true,
|
||||
})
|
||||
|
||||
await instance.initialize()
|
||||
|
||||
if (process.env.lb_service && process.send) {
|
||||
process.send({
|
||||
status: "ready"
|
||||
})
|
||||
}
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
try {
|
||||
// Apply patches
|
||||
registerPatches()
|
||||
|
||||
// Apply aliases
|
||||
registerAliases()
|
||||
|
||||
// execute main
|
||||
Module.runMain()
|
||||
} catch (error) {
|
||||
console.error("[BOOT] ❌ Boot error: ", error)
|
||||
}
|
49
server/bootloader/bin
Executable file
49
server/bootloader/bin
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env node
|
||||
const path = require("node:path")
|
||||
const childProcess = require("node:child_process")
|
||||
const startFileWatcher = require("./startFileWatcher.js")
|
||||
|
||||
const bootloaderPath = path.resolve(__dirname, "boot.js")
|
||||
const mainModulePath = process.argv[2]
|
||||
const mainModuleSrc = path.resolve(process.cwd(), path.dirname(mainModulePath))
|
||||
|
||||
let childProcessInstance = null
|
||||
let reloadTimeout = null
|
||||
|
||||
function selfReload() {
|
||||
if (!childProcessInstance) {
|
||||
console.error(
|
||||
"[BOOT] Cannot self-reload. Missing childProcessInstance.",
|
||||
)
|
||||
return process.exit(0)
|
||||
}
|
||||
|
||||
console.log("[BOOT] Reloading...")
|
||||
|
||||
childProcessInstance.kill()
|
||||
|
||||
boot()
|
||||
}
|
||||
|
||||
function selfReloadDebounce() {
|
||||
if (reloadTimeout) {
|
||||
clearTimeout(reloadTimeout)
|
||||
}
|
||||
|
||||
reloadTimeout = setTimeout(selfReload, 300)
|
||||
}
|
||||
|
||||
function boot() {
|
||||
childProcessInstance = childProcess.fork(bootloaderPath, [mainModulePath], {
|
||||
stdio: "inherit",
|
||||
})
|
||||
}
|
||||
|
||||
// if --watch flag exist, start file watcher
|
||||
if (process.argv.includes("--watch")) {
|
||||
startFileWatcher(mainModuleSrc, {
|
||||
onReload: selfReloadDebounce,
|
||||
})
|
||||
}
|
||||
|
||||
boot()
|
55
server/bootloader/boot.js
Normal file
55
server/bootloader/boot.js
Normal file
@ -0,0 +1,55 @@
|
||||
require("dotenv").config()
|
||||
require("sucrase/register")
|
||||
|
||||
const path = require("node:path")
|
||||
const Module = require("node:module")
|
||||
const registerBaseAliases = require("./registerBaseAliases")
|
||||
|
||||
// Override file execution arg
|
||||
process.argv.splice(1, 1)
|
||||
process.argv[1] = path.resolve(process.argv[1])
|
||||
|
||||
// Expose to global
|
||||
global.paths = {
|
||||
root: process.cwd(),
|
||||
__src: path.resolve(process.cwd(), path.dirname(process.argv[1])),
|
||||
}
|
||||
|
||||
global["aliases"] = {
|
||||
// expose src
|
||||
"@": global.paths.__src,
|
||||
|
||||
// expose shared resources
|
||||
"@db": path.resolve(process.cwd(), "db_models"),
|
||||
"@db_models": path.resolve(process.cwd(), "db_models"),
|
||||
"@shared-utils": path.resolve(process.cwd(), "utils"),
|
||||
"@shared-classes": path.resolve(process.cwd(), "classes"),
|
||||
"@shared-lib": path.resolve(process.cwd(), "lib"),
|
||||
"@shared-middlewares": path.resolve(process.cwd(), "middlewares"),
|
||||
|
||||
// expose internal resources
|
||||
"@routes": path.resolve(paths.__src, "routes"),
|
||||
"@models": path.resolve(paths.__src, "models"),
|
||||
"@middlewares": path.resolve(paths.__src, "middlewares"),
|
||||
"@classes": path.resolve(paths.__src, "classes"),
|
||||
"@services": path.resolve(paths.__src, "services"),
|
||||
"@config": path.resolve(paths.__src, "config"),
|
||||
"@utils": path.resolve(paths.__src, "utils"),
|
||||
"@lib": path.resolve(paths.__src, "lib"),
|
||||
}
|
||||
|
||||
// expose bootwrapper to global
|
||||
global.Boot = require("./bootWrapper")
|
||||
|
||||
try {
|
||||
// apply patches
|
||||
require("./patches.js")
|
||||
|
||||
// Apply aliases
|
||||
registerBaseAliases(global.paths.__src, global["aliases"])
|
||||
|
||||
// execute main
|
||||
Module.runMain()
|
||||
} catch (error) {
|
||||
console.error("[BOOT] ❌ Boot error: ", error)
|
||||
}
|
48
server/bootloader/bootWrapper.js
Normal file
48
server/bootloader/bootWrapper.js
Normal file
@ -0,0 +1,48 @@
|
||||
const { onExit } = require("signal-exit")
|
||||
const injectEnvFromInfisical = require("./injectEnvFromInfisical")
|
||||
|
||||
module.exports = async function Boot(main) {
|
||||
if (!main) {
|
||||
throw new Error("main class is not defined")
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[BOOT] Booting in [${global.isProduction ? "production" : "development"}] mode...`,
|
||||
)
|
||||
|
||||
if (
|
||||
process.env.INFISICAL_CLIENT_ID &&
|
||||
process.env.INFISICAL_CLIENT_SECRET
|
||||
) {
|
||||
console.log(
|
||||
`[BOOT] INFISICAL Credentials found, injecting env variables from INFISICAL...`,
|
||||
)
|
||||
await injectEnvFromInfisical()
|
||||
}
|
||||
|
||||
const instance = new main()
|
||||
|
||||
process.on("exit", (code) => {
|
||||
console.log(`[BOOT] Closing ...`)
|
||||
|
||||
instance._fireClose()
|
||||
})
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
process.on("SIGINT", () => {
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
await instance.initialize()
|
||||
|
||||
if (process.env.lb_service && process.send) {
|
||||
process.send({
|
||||
status: "ready",
|
||||
})
|
||||
}
|
||||
|
||||
return instance
|
||||
}
|
32
server/bootloader/injectEnvFromInfisical.js
Normal file
32
server/bootloader/injectEnvFromInfisical.js
Normal file
@ -0,0 +1,32 @@
|
||||
const { InfisicalClient } = require("@infisical/sdk")
|
||||
|
||||
module.exports = async function injectEnvFromInfisical() {
|
||||
const envMode = (global.FORCE_ENV ?? global.isProduction) ? "prod" : "dev"
|
||||
|
||||
console.log(
|
||||
`[BOOT] 🔑 Injecting env variables from INFISICAL in [${envMode}] mode...`,
|
||||
)
|
||||
|
||||
const client = new InfisicalClient({
|
||||
auth: {
|
||||
universalAuth: {
|
||||
clientId: process.env.INFISICAL_CLIENT_ID,
|
||||
clientSecret: process.env.INFISICAL_CLIENT_SECRET,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const secrets = await client.listSecrets({
|
||||
environment: envMode,
|
||||
path: process.env.INFISICAL_PATH ?? "/",
|
||||
projectId: process.env.INFISICAL_PROJECT_ID ?? null,
|
||||
includeImports: false,
|
||||
})
|
||||
|
||||
//inject to process.env
|
||||
secrets.forEach((secret) => {
|
||||
if (!process.env[secret.secretKey]) {
|
||||
process.env[secret.secretKey] = secret.secretValue
|
||||
}
|
||||
})
|
||||
}
|
49
server/bootloader/patches.js
Normal file
49
server/bootloader/patches.js
Normal file
@ -0,0 +1,49 @@
|
||||
const { webcrypto: crypto } = require("node:crypto")
|
||||
const { Buffer } = require("node:buffer")
|
||||
|
||||
global.isProduction = process.env.NODE_ENV === "production"
|
||||
|
||||
global.b64Decode = (data) => {
|
||||
return Buffer.from(data, "base64").toString("utf-8")
|
||||
}
|
||||
global.b64Encode = (data) => {
|
||||
return Buffer.from(data, "utf-8").toString("base64")
|
||||
}
|
||||
|
||||
global.nanoid = (t = 21) =>
|
||||
crypto
|
||||
.getRandomValues(new Uint8Array(t))
|
||||
.reduce(
|
||||
(t, e) =>
|
||||
(t +=
|
||||
(e &= 63) < 36
|
||||
? e.toString(36)
|
||||
: e < 62
|
||||
? (e - 26).toString(36).toUpperCase()
|
||||
: e > 62
|
||||
? "-"
|
||||
: "_"),
|
||||
"",
|
||||
)
|
||||
|
||||
Array.prototype.updateFromObjectKeys = function (obj) {
|
||||
this.forEach((value, index) => {
|
||||
if (obj[value] !== undefined) {
|
||||
this[index] = obj[value]
|
||||
}
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
global.ToBoolean = (value) => {
|
||||
if (typeof value === "boolean") {
|
||||
return value
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
return value.toLowerCase() === "true"
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
14
server/bootloader/registerBaseAliases.js
Normal file
14
server/bootloader/registerBaseAliases.js
Normal file
@ -0,0 +1,14 @@
|
||||
const path = require("node:path")
|
||||
const moduleAlias = require("module-alias")
|
||||
|
||||
module.exports = function registerBaseAliases(fromPath, customAliases = {}) {
|
||||
if (typeof fromPath === "undefined") {
|
||||
if (module.parent.filename.includes("dist")) {
|
||||
fromPath = path.resolve(process.cwd(), "dist")
|
||||
} else {
|
||||
fromPath = path.resolve(process.cwd(), "src")
|
||||
}
|
||||
}
|
||||
|
||||
moduleAlias.addAliases(customAliases)
|
||||
}
|
29
server/bootloader/startFileWatcher.js
Normal file
29
server/bootloader/startFileWatcher.js
Normal file
@ -0,0 +1,29 @@
|
||||
const chokidar = require("chokidar")
|
||||
const { minimatch } = require("minimatch")
|
||||
|
||||
const defaultIgnored = [
|
||||
"**/.cache/**",
|
||||
"**/node_modules/**",
|
||||
"**/dist/**",
|
||||
"**/build/**",
|
||||
]
|
||||
|
||||
module.exports = async (fromPath, { onReload }) => {
|
||||
console.log("[WATCHER] Starting watching path >", fromPath)
|
||||
|
||||
global._watcher = chokidar.watch(fromPath, {
|
||||
ignored: (path) =>
|
||||
defaultIgnored.some((pattern) => minimatch(path, pattern)),
|
||||
persistent: true,
|
||||
ignoreInitial: true,
|
||||
awaitWriteFinish: true,
|
||||
})
|
||||
|
||||
global._watcher.on("all", (event, filePath) => {
|
||||
console.log(`[WATCHER] Event [${event}] > ${filePath}`)
|
||||
|
||||
if (typeof onReload === "function") {
|
||||
onReload()
|
||||
}
|
||||
})
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "linebridge",
|
||||
"version": "0.26.0",
|
||||
"version": "1.0.0",
|
||||
"description": "Multiproposal framework to build fast, scalable, and secure servers.",
|
||||
"author": "RageStudio <support@ragestudio.net>",
|
||||
"bugs": {
|
||||
@ -9,7 +9,7 @@
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"bin": {
|
||||
"linebridge-boot": "./bin/boot.js"
|
||||
"linebridge-boot": "./bootloader/bin"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@ -20,32 +20,26 @@
|
||||
"./package.json"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "hermes-node ./src/bin/server.js",
|
||||
"build": "hermes build --parallel --clean",
|
||||
"test": "mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"@foxify/events": "^2.1.0",
|
||||
"@gullerya/object-observer": "^6.1.3",
|
||||
"@infisical/sdk": "^2.1.8",
|
||||
"@socket.io/cluster-adapter": "^0.2.2",
|
||||
"@socket.io/redis-adapter": "^8.2.1",
|
||||
"@socket.io/redis-emitter": "^5.1.0",
|
||||
"@socket.io/sticky": "^1.0.4",
|
||||
"axios": "^1.6.7",
|
||||
"axios-retry": "3.4.0",
|
||||
"cors": "2.8.5",
|
||||
"dotenv": "^16.4.4",
|
||||
"axios": "^1.8.4",
|
||||
"chokidar": "^4.0.3",
|
||||
"dotenv": "^16.5.0",
|
||||
"hyper-express": "^6.17.3",
|
||||
"ioredis": "^5.3.2",
|
||||
"md5": "^2.3.0",
|
||||
"module-alias": "2.2.2",
|
||||
"morgan": "1.10.0",
|
||||
"ioredis": "^5.6.1",
|
||||
"minimatch": "^10.0.1",
|
||||
"module-alias": "^2.2.2",
|
||||
"signal-exit": "^4.1.0",
|
||||
"socket.io": "^4.8.1",
|
||||
"socket.io-client": "^4.5.4",
|
||||
"sucrase": "^3.35.0",
|
||||
"uuid": "^9.0.1"
|
||||
"sucrase": "^3.35.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ragestudio/hermes": "^1.0.0",
|
||||
|
@ -1,21 +0,0 @@
|
||||
import path from "node:path"
|
||||
|
||||
import Endpoint from "../classes/endpoint"
|
||||
import defaults from "../defaults"
|
||||
|
||||
const projectPkg = require(path.resolve(process.cwd(), "package.json"))
|
||||
|
||||
export default class MainEndpoint extends Endpoint {
|
||||
route = "/"
|
||||
|
||||
get = async (req, res) => {
|
||||
return {
|
||||
name: globalThis._linebridge.name ?? "unknown",
|
||||
version: projectPkg.version ?? "unknown",
|
||||
engine: globalThis._linebridge.useEngine ?? "unknown",
|
||||
request_time: new Date().getTime(),
|
||||
lb_version: defaults.version ?? "unknown",
|
||||
experimental: defaults.isExperimental.toString() ?? "unknown",
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import Endpoint from "../classes/endpoint"
|
||||
|
||||
export default class MainEndpoint extends Endpoint {
|
||||
route = "/_map"
|
||||
|
||||
get = async (req, res) => {
|
||||
const httpMap = Object.entries(this.server.engine.router.map).reduce((acc, [route, { method, path }]) => {
|
||||
if (!acc[method]) {
|
||||
acc[method] = []
|
||||
}
|
||||
|
||||
acc[method].push({
|
||||
route: path
|
||||
})
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return res.json({
|
||||
http: httpMap,
|
||||
websocket: []
|
||||
})
|
||||
}
|
||||
}
|
20
server/src/baseRoutes/main.js
Executable file
20
server/src/baseRoutes/main.js
Executable file
@ -0,0 +1,20 @@
|
||||
import path from "node:path"
|
||||
|
||||
import Route from "../classes/Route"
|
||||
import Vars from "../vars"
|
||||
|
||||
export default class MainRoute extends Route {
|
||||
static route = "/"
|
||||
static useContexts = ["server"]
|
||||
|
||||
get = async (req, res, ctx) => {
|
||||
return {
|
||||
name: ctx.server.params.refName ?? "unknown",
|
||||
version: Vars.projectPkg.version ?? "unknown",
|
||||
engine: ctx.server.params.useEngine ?? "unknown",
|
||||
lb_version: Vars.libPkg.version ?? "unknown",
|
||||
experimental: ctx.server.isExperimental ?? "unknown",
|
||||
request_time: new Date().getTime(),
|
||||
}
|
||||
}
|
||||
}
|
27
server/src/baseRoutes/map.js
Executable file
27
server/src/baseRoutes/map.js
Executable file
@ -0,0 +1,27 @@
|
||||
import Route from "../classes/Route"
|
||||
|
||||
export default class MapRoute extends Route {
|
||||
static route = "/_map"
|
||||
|
||||
get = async (req, res) => {
|
||||
const httpMap = Array.from(this.server.engine.map.entries()).reduce(
|
||||
(acc, [route, { method, path }]) => {
|
||||
if (!acc[method]) {
|
||||
acc[method] = []
|
||||
}
|
||||
|
||||
acc[method].push({
|
||||
route: path,
|
||||
})
|
||||
|
||||
return acc
|
||||
},
|
||||
{},
|
||||
)
|
||||
|
||||
return res.json({
|
||||
http: httpMap,
|
||||
websocket: [],
|
||||
})
|
||||
}
|
||||
}
|
17
server/src/classes/Endpoint/index.js
Normal file
17
server/src/classes/Endpoint/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { HttpRequestHandler } from "../Handler"
|
||||
|
||||
export default class Endpoint {
|
||||
static _constructed = false
|
||||
static _class = true
|
||||
|
||||
constructor(method, context) {
|
||||
this._constructed = true
|
||||
this.context = context
|
||||
|
||||
if (typeof method === "function") {
|
||||
this.run = method
|
||||
}
|
||||
|
||||
this.handler = new HttpRequestHandler(this.run, this.context)
|
||||
}
|
||||
}
|
55
server/src/classes/Handler/index.js
Normal file
55
server/src/classes/Handler/index.js
Normal file
@ -0,0 +1,55 @@
|
||||
export class Handler {
|
||||
constructor(fn, ctx) {
|
||||
this.fn = fn ?? (() => Promise.resolve())
|
||||
this.ctx = ctx ?? {}
|
||||
|
||||
this.fn = this.fn.bind({
|
||||
contexts: this.ctx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class HttpRequestHandler extends Handler {
|
||||
constructor(fn, ctx) {
|
||||
super(fn, ctx)
|
||||
|
||||
return this.exec
|
||||
}
|
||||
|
||||
exec = async (req, res) => {
|
||||
try {
|
||||
req.ctx = this.ctx
|
||||
const result = await this.fn(req, res, this.ctx)
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
} catch (error) {
|
||||
// handle if is a operation error
|
||||
if (error instanceof OperationError) {
|
||||
return res.status(error.code).json({
|
||||
error: error.message,
|
||||
})
|
||||
}
|
||||
|
||||
// if is not a operation error, that is a exception.
|
||||
// gonna handle like a generic 500 error
|
||||
console.error({
|
||||
message: "Unhandled route error:",
|
||||
description: error.stack,
|
||||
})
|
||||
|
||||
return res.status(500).json({
|
||||
error: error.message,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement MiddlewareHandler
|
||||
export class MiddlewareHandler extends Handler {}
|
||||
|
||||
// TODO: Implement WebsocketRequestHandler
|
||||
export class WebsocketRequestHandler extends Handler {}
|
||||
|
||||
export default Handler
|
@ -1,159 +1,162 @@
|
||||
import { EventEmitter } from "@foxify/events"
|
||||
|
||||
export default class IPCClient {
|
||||
constructor(self, _process) {
|
||||
this.self = self
|
||||
this.process = _process
|
||||
constructor(self, _process) {
|
||||
this.self = self
|
||||
this.process = _process
|
||||
|
||||
this.process.on("message", (msg) => {
|
||||
if (typeof msg !== "object") {
|
||||
// not an IPC message, ignore
|
||||
return false
|
||||
}
|
||||
this.process.on("message", (msg) => {
|
||||
if (typeof msg !== "object") {
|
||||
// not an IPC message, ignore
|
||||
return false
|
||||
}
|
||||
|
||||
const { event, payload } = msg
|
||||
const { event, payload } = msg
|
||||
|
||||
if (!event || !event.startsWith("ipc:")) {
|
||||
return false
|
||||
}
|
||||
if (!event || !event.startsWith("ipc:")) {
|
||||
return false
|
||||
}
|
||||
|
||||
//console.log(`[IPC:CLIENT] Received event [${event}] from [${payload.from}]`)
|
||||
//console.log(`[IPC:CLIENT] Received event [${event}] from [${payload.from}]`)
|
||||
|
||||
if (event.startsWith("ipc:exec")) {
|
||||
return this.handleExecution(payload)
|
||||
}
|
||||
if (event.startsWith("ipc:exec")) {
|
||||
return this.handleExecution(payload)
|
||||
}
|
||||
|
||||
if (event.startsWith("ipc:akn")) {
|
||||
return this.handleAcknowledgement(payload)
|
||||
}
|
||||
})
|
||||
}
|
||||
if (event.startsWith("ipc:akn")) {
|
||||
return this.handleAcknowledgement(payload)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
eventBus = new EventEmitter()
|
||||
eventBus = new EventEmitter()
|
||||
|
||||
handleExecution = async (payload) => {
|
||||
let { id, command, args, from } = payload
|
||||
handleExecution = async (payload) => {
|
||||
if (typeof this.self.ipcEvents !== "object") {
|
||||
return null
|
||||
}
|
||||
|
||||
let fn = this.self.ipcEvents[command]
|
||||
let { id, command, args, from } = payload
|
||||
|
||||
if (!fn) {
|
||||
this.process.send({
|
||||
event: `ipc:akn:${id}`,
|
||||
payload: {
|
||||
target: from,
|
||||
from: this.self.constructor.refName,
|
||||
let fn = this.self.ipcEvents[command]
|
||||
|
||||
id: id,
|
||||
error: `IPC: Command [${command}] not found`,
|
||||
}
|
||||
})
|
||||
if (typeof fn !== "function") {
|
||||
this.process.send({
|
||||
event: `ipc:akn:${id}`,
|
||||
payload: {
|
||||
target: from,
|
||||
from: this.self.params.refName,
|
||||
|
||||
return false
|
||||
}
|
||||
id: id,
|
||||
error: `IPC: Command [${command}] not found`,
|
||||
},
|
||||
})
|
||||
|
||||
try {
|
||||
let result = await fn(this.self.contexts, ...args)
|
||||
return false
|
||||
}
|
||||
|
||||
this.process.send({
|
||||
event: `ipc:akn:${id}`,
|
||||
payload: {
|
||||
target: from,
|
||||
from: this.self.constructor.refName,
|
||||
try {
|
||||
let result = await fn(this.self.contexts, ...args)
|
||||
|
||||
id: id,
|
||||
result: result,
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
this.process.send({
|
||||
event: `ipc:akn:${id}`,
|
||||
payload: {
|
||||
target: from,
|
||||
from: this.self.constructor.refName,
|
||||
this.process.send({
|
||||
event: `ipc:akn:${id}`,
|
||||
payload: {
|
||||
target: from,
|
||||
from: this.self.params.refName,
|
||||
|
||||
id: id,
|
||||
error: error.message,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
id: id,
|
||||
result: result,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
this.process.send({
|
||||
event: `ipc:akn:${id}`,
|
||||
payload: {
|
||||
target: from,
|
||||
from: this.self.params.refName,
|
||||
|
||||
handleAcknowledgement = async (payload) => {
|
||||
let { id, result, error } = payload
|
||||
id: id,
|
||||
error: error.message,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.eventBus.emit(`ipc:akn:${id}`, {
|
||||
id: id,
|
||||
result: result,
|
||||
error: error,
|
||||
})
|
||||
}
|
||||
handleAcknowledgement = async (payload) => {
|
||||
let { id, result, error } = payload
|
||||
|
||||
// call a command on a remote service, and waits to get a response from akn (async)
|
||||
call = async (to_service_id, command, ...args) => {
|
||||
const remote_call_id = Date.now()
|
||||
this.eventBus.emit(`ipc:akn:${id}`, {
|
||||
id: id,
|
||||
result: result,
|
||||
error: error,
|
||||
})
|
||||
}
|
||||
|
||||
//console.debug(`[IPC:CLIENT] Invoking command [${command}] on service [${to_service_id}]`)
|
||||
// call a command on a remote service, and waits to get a response from akn (async)
|
||||
call = async (to_service_id, command, ...args) => {
|
||||
const remote_call_id = Date.now()
|
||||
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
try {
|
||||
//console.debug(`[IPC:CLIENT] Invoking command [${command}] on service [${to_service_id}]`)
|
||||
|
||||
this.process.send({
|
||||
event: "ipc:exec",
|
||||
payload: {
|
||||
target: to_service_id,
|
||||
from: this.self.constructor.refName,
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
try {
|
||||
this.process.send({
|
||||
event: "ipc:exec",
|
||||
payload: {
|
||||
target: to_service_id,
|
||||
from: this.self.params.refName,
|
||||
|
||||
id: remote_call_id,
|
||||
command,
|
||||
args,
|
||||
}
|
||||
})
|
||||
id: remote_call_id,
|
||||
command,
|
||||
args,
|
||||
},
|
||||
})
|
||||
|
||||
this.eventBus.once(`ipc:akn:${remote_call_id}`, resolve)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
this.eventBus.once(`ipc:akn:${remote_call_id}`, resolve)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
reject(error)
|
||||
}
|
||||
}).catch((error) => {
|
||||
return {
|
||||
error: error
|
||||
}
|
||||
})
|
||||
reject(error)
|
||||
}
|
||||
}).catch((error) => {
|
||||
return {
|
||||
error: error,
|
||||
}
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
throw new OperationError(500, response.error)
|
||||
}
|
||||
if (response.error) {
|
||||
throw new OperationError(500, response.error)
|
||||
}
|
||||
|
||||
return response.result
|
||||
}
|
||||
return response.result
|
||||
}
|
||||
|
||||
// call a command on a remote service, but return it immediately
|
||||
invoke = async (to_service_id, command, ...args) => {
|
||||
const remote_call_id = Date.now()
|
||||
// call a command on a remote service, but return it immediately
|
||||
invoke = async (to_service_id, command, ...args) => {
|
||||
const remote_call_id = Date.now()
|
||||
|
||||
try {
|
||||
this.process.send({
|
||||
event: "ipc:exec",
|
||||
payload: {
|
||||
target: to_service_id,
|
||||
from: this.self.constructor.refName,
|
||||
try {
|
||||
this.process.send({
|
||||
event: "ipc:exec",
|
||||
payload: {
|
||||
target: to_service_id,
|
||||
from: this.self.params.refName,
|
||||
|
||||
id: remote_call_id,
|
||||
command,
|
||||
args,
|
||||
}
|
||||
})
|
||||
id: remote_call_id,
|
||||
command,
|
||||
args,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
id: remote_call_id
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {
|
||||
id: remote_call_id,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
return {
|
||||
error: error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
error: error,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
58
server/src/classes/Route/index.js
Executable file
58
server/src/classes/Route/index.js
Executable file
@ -0,0 +1,58 @@
|
||||
import Endpoint from "../Endpoint"
|
||||
|
||||
export default class Route {
|
||||
constructor(server, params = {}) {
|
||||
if (!server) {
|
||||
throw new Error("server is not defined")
|
||||
}
|
||||
|
||||
this.server = server
|
||||
this.params = {
|
||||
route: this.constructor.route ?? "/",
|
||||
useContexts: this.constructor.useContexts ?? [],
|
||||
useMiddlewares: this.constructor.useMiddlewares ?? [],
|
||||
...params,
|
||||
}
|
||||
|
||||
if (typeof this.params.handlers === "object") {
|
||||
for (const method of global._linebridge.params.httpMethods) {
|
||||
if (typeof this.params.handlers[method] !== "function") {
|
||||
continue
|
||||
}
|
||||
|
||||
this[method] = this.params.handlers[method]
|
||||
}
|
||||
}
|
||||
|
||||
if (this.server.contexts && Array.isArray(this.params.useContexts)) {
|
||||
for (const key of this.params.useContexts) {
|
||||
this.ctx[key] = this.server.contexts[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx = {}
|
||||
|
||||
register = () => {
|
||||
for (const method of global._linebridge.params.httpMethods) {
|
||||
if (typeof this[method] === "undefined") {
|
||||
continue
|
||||
}
|
||||
|
||||
if (!(this[method] instanceof Endpoint)) {
|
||||
if (this[method]._class && !this[method]._constructed) {
|
||||
this[method] = new this[method](undefined, this.ctx)
|
||||
} else {
|
||||
this[method] = new Endpoint(this[method], this.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
this.server.register.http({
|
||||
method: method,
|
||||
route: this.params.route,
|
||||
middlewares: this.params.useMiddlewares,
|
||||
fn: this[method].handler,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -167,7 +167,7 @@ class RTEngineNG {
|
||||
}
|
||||
}
|
||||
|
||||
attach = async (engine) => {
|
||||
attach = (engine) => {
|
||||
this.engine = engine
|
||||
|
||||
this.engine.app.ws(this.config.path ?? `/`, this.handleConnection)
|
@ -1,87 +0,0 @@
|
||||
export default class Endpoint {
|
||||
constructor(server, params = {}, ctx = {}) {
|
||||
this.server = server
|
||||
this.params = params
|
||||
this.ctx = ctx
|
||||
|
||||
if (!server) {
|
||||
throw new Error("Server is not defined")
|
||||
}
|
||||
|
||||
this.route = this.route ?? this.constructor.route ?? this.params.route
|
||||
this.enabled = this.enabled ?? this.constructor.enabled ?? this.params.enabled ?? true
|
||||
|
||||
this.middlewares = [
|
||||
...this.middlewares ?? [],
|
||||
...this.params.middlewares ?? [],
|
||||
]
|
||||
|
||||
if (this.params.handlers) {
|
||||
for (const method of globalThis._linebridge.validHttpMethods) {
|
||||
if (typeof this.params.handlers[method] === "function") {
|
||||
this[method] = this.params.handlers[method]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.selfRegister()
|
||||
|
||||
if (Array.isArray(this.params.useContexts)) {
|
||||
for (const contextRef of this.params.useContexts) {
|
||||
this.endpointContext[contextRef] = this.server.contexts[contextRef]
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
endpointContext = {}
|
||||
|
||||
createHandler(fn) {
|
||||
fn = fn.bind(this.server)
|
||||
|
||||
return async (req, res) => {
|
||||
try {
|
||||
const result = await fn(req, res, this.endpointContext)
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof OperationError) {
|
||||
return res.status(error.code).json({
|
||||
"error": error.message
|
||||
})
|
||||
}
|
||||
|
||||
console.error({
|
||||
message: "Unhandled route error:",
|
||||
description: error.stack,
|
||||
})
|
||||
|
||||
return res.status(500).json({
|
||||
"error": error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selfRegister = async () => {
|
||||
for await (const method of globalThis._linebridge.validHttpMethods) {
|
||||
const methodHandler = this[method]
|
||||
|
||||
if (typeof methodHandler !== "undefined") {
|
||||
const fn = this.createHandler(this[method].fn ?? this[method])
|
||||
|
||||
this.server.register.http(
|
||||
{
|
||||
method,
|
||||
route: this.route,
|
||||
middlewares: this.middlewares,
|
||||
fn: fn,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
Controller: require("./controller"),
|
||||
Endpoint: require("./endpoint"),
|
||||
RTEngine: require("./rtengine"),
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
const os = require("os")
|
||||
const packageJSON = require(path.resolve(module.path, "../package.json"))
|
||||
|
||||
function getHostAddress() {
|
||||
const interfaces = os.networkInterfaces()
|
||||
|
||||
for (const key in interfaces) {
|
||||
const iface = interfaces[key]
|
||||
|
||||
for (let index = 0; index < iface.length; index++) {
|
||||
const alias = iface[index]
|
||||
|
||||
if (alias.family === "IPv4" && alias.address !== "127.0.0.1" && !alias.internal) {
|
||||
return alias.address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "0.0.0.0"
|
||||
}
|
||||
|
||||
export default {
|
||||
isExperimental: fs.existsSync(path.resolve(module.path, "../.experimental")),
|
||||
version: packageJSON.version,
|
||||
localhost_address: getHostAddress() ?? "localhost",
|
||||
params: {
|
||||
urlencoded: true,
|
||||
engine: "express",
|
||||
http_protocol: "http",
|
||||
ws_protocol: "ws",
|
||||
},
|
||||
headers: {
|
||||
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Authorization",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS, PUT, PATCH, DELETE, DEL",
|
||||
"Access-Control-Allow-Credentials": "true",
|
||||
},
|
||||
middlewares: {
|
||||
cors: require("./middlewares/cors").default,
|
||||
logs: require("./middlewares/logger").default,
|
||||
},
|
||||
useMiddlewares: [
|
||||
//"cors",
|
||||
"logs",
|
||||
],
|
||||
controllers: [],
|
||||
fixed_http_methods: {
|
||||
"del": "delete",
|
||||
},
|
||||
valid_http_methods: [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"patch",
|
||||
"del",
|
||||
"delete",
|
||||
"trace",
|
||||
"head",
|
||||
"any",
|
||||
"options",
|
||||
"ws",
|
||||
],
|
||||
}
|
80
server/src/engines/he-legacy/index.js
Executable file
80
server/src/engines/he-legacy/index.js
Executable file
@ -0,0 +1,80 @@
|
||||
import he from "hyper-express"
|
||||
import rtengine from "./rtengine"
|
||||
|
||||
export default class Engine {
|
||||
constructor(server) {
|
||||
this.server = server
|
||||
}
|
||||
|
||||
static heDefaultParams = {
|
||||
max_body_length: 50 * 1024 * 1024, //50MB in bytes,
|
||||
}
|
||||
|
||||
app = null
|
||||
ws = null
|
||||
router = new he.Router()
|
||||
map = new Map()
|
||||
|
||||
initialize = async () => {
|
||||
this.app = new he.Server({
|
||||
...Engine.heDefaultParams,
|
||||
key_file_name: this.server.ssl?.key ?? undefined,
|
||||
cert_file_name: this.server.ssl?.cert ?? undefined,
|
||||
})
|
||||
|
||||
this.router.any("*", this.defaultResponse)
|
||||
this.app.use(this.mainMiddleware)
|
||||
this.app.use(this.router)
|
||||
|
||||
if (this.server.params.websockets === true) {
|
||||
this.ws = new rtengine({
|
||||
requireAuth: this.server.constructor.requiredWsAuth,
|
||||
handleAuth: this.server.handleWsAuth,
|
||||
root: `/${this.server.params.refName}`,
|
||||
})
|
||||
|
||||
this.ws.initialize()
|
||||
|
||||
global.websockets = this.ws
|
||||
|
||||
await this.ws.io.attachApp(this.app.uws_instance)
|
||||
}
|
||||
}
|
||||
|
||||
mainMiddleware = async (req, res, next) => {
|
||||
if (req.method === "OPTIONS") {
|
||||
return res.status(204).end()
|
||||
}
|
||||
|
||||
// register body parser
|
||||
if (req.headers["content-type"]) {
|
||||
if (
|
||||
!req.headers["content-type"].startsWith("multipart/form-data")
|
||||
) {
|
||||
req.body = await req.urlencoded()
|
||||
req.body = await req.json(req.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultResponse = (req, res) => {
|
||||
return res.status(404).json({
|
||||
error: "Not found",
|
||||
})
|
||||
}
|
||||
|
||||
listen = async () => {
|
||||
await this.app.listen(this.server.params.listenPort)
|
||||
}
|
||||
|
||||
// close must be synchronous
|
||||
close = () => {
|
||||
if (this.ws && typeof this.ws.close === "function") {
|
||||
this.ws.close()
|
||||
}
|
||||
|
||||
if (this.app && typeof this.app.close === "function") {
|
||||
this.app.close()
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import redis from "ioredis"
|
||||
import SocketIO from "socket.io"
|
||||
import { EventEmitter } from "@foxify/events"
|
||||
|
||||
import RedisMap from "../../lib/redis_map"
|
||||
import RedisMap from "./redis_map.js"
|
||||
|
||||
export default class RTEngineServer {
|
||||
constructor(params = {}) {
|
81
server/src/engines/he/index.js
Executable file
81
server/src/engines/he/index.js
Executable file
@ -0,0 +1,81 @@
|
||||
import he from "hyper-express"
|
||||
import RtEngine from "../../classes/RtEngine"
|
||||
|
||||
export default class Engine {
|
||||
constructor(server) {
|
||||
this.server = server
|
||||
}
|
||||
|
||||
static heDefaultParams = {
|
||||
max_body_length: 50 * 1024 * 1024, //50MB in bytes,
|
||||
}
|
||||
|
||||
app = null
|
||||
ws = null
|
||||
router = new he.Router()
|
||||
map = new Map()
|
||||
|
||||
initialize = async () => {
|
||||
this.app = new he.Server({
|
||||
...Engine.heDefaultParams,
|
||||
key_file_name: this.server.ssl?.key ?? undefined,
|
||||
cert_file_name: this.server.ssl?.cert ?? undefined,
|
||||
})
|
||||
|
||||
this.router.any("*", this.defaultResponse)
|
||||
this.app.use(this.mainMiddleware)
|
||||
this.app.use(this.router)
|
||||
|
||||
if (this.server.params.websockets === true) {
|
||||
this.ws = new RtEngine({
|
||||
path:
|
||||
this.server.params.wsPath ??
|
||||
`/${this.server.params.refName}`,
|
||||
onUpgrade: this.server.handleWsUpgrade,
|
||||
onConnection: this.server.handleWsConnection,
|
||||
onDisconnect: this.server.handleWsDisconnect,
|
||||
})
|
||||
|
||||
global.websockets = this.ws
|
||||
|
||||
this.ws.attach(this)
|
||||
}
|
||||
}
|
||||
|
||||
mainMiddleware = async (req, res, next) => {
|
||||
if (req.method === "OPTIONS") {
|
||||
return res.status(204).end()
|
||||
}
|
||||
|
||||
// register body parser
|
||||
if (req.headers["content-type"]) {
|
||||
if (
|
||||
!req.headers["content-type"].startsWith("multipart/form-data")
|
||||
) {
|
||||
req.body = await req.urlencoded()
|
||||
req.body = await req.json(req.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultResponse = (req, res) => {
|
||||
return res.status(404).json({
|
||||
error: "Not found",
|
||||
})
|
||||
}
|
||||
|
||||
listen = async () => {
|
||||
await this.app.listen(this.server.params.listenPort)
|
||||
}
|
||||
|
||||
// close must be synchronous
|
||||
close = () => {
|
||||
if (this.ws && typeof this.ws.close === "function") {
|
||||
this.ws.close()
|
||||
}
|
||||
|
||||
if (this.app && typeof this.app.close === "function") {
|
||||
this.app.close()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
import he from "hyper-express"
|
||||
import rtengineng from "../../classes/rtengineng"
|
||||
|
||||
export default class HyperExpressEngineNG {
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx
|
||||
}
|
||||
|
||||
app = null
|
||||
ws = null
|
||||
router = null
|
||||
|
||||
initialize = async () => {
|
||||
console.warn(
|
||||
`hyper-express-ng is a experimental engine, some features may not be available or work properly!`,
|
||||
)
|
||||
|
||||
const appParams = {
|
||||
max_body_length: 50 * 1024 * 1024, //50MB in bytes,
|
||||
}
|
||||
|
||||
if (this.ctx.ssl) {
|
||||
appParams.key_file_name = this.ctx.ssl?.key ?? null
|
||||
appParams.cert_file_name = this.ctx.ssl?.cert ?? null
|
||||
}
|
||||
|
||||
this.app = new he.Server(appParams)
|
||||
|
||||
this.router = new he.Router()
|
||||
|
||||
// create a router map
|
||||
if (typeof this.router.map !== "object") {
|
||||
this.router.map = {}
|
||||
}
|
||||
|
||||
await this.router.any("*", (req, res) => {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
message: "Not found",
|
||||
})
|
||||
})
|
||||
|
||||
await this.app.use(async (req, res, next) => {
|
||||
if (req.method === "OPTIONS") {
|
||||
// handle cors
|
||||
if (this.ctx.constructor.ignoreCors) {
|
||||
res.setHeader("Access-Control-Allow-Methods", "*")
|
||||
res.setHeader("Access-Control-Allow-Origin", "*")
|
||||
res.setHeader("Access-Control-Allow-Headers", "*")
|
||||
}
|
||||
|
||||
return res.status(204).end()
|
||||
}
|
||||
|
||||
// register body parser
|
||||
if (req.headers["content-type"]) {
|
||||
if (
|
||||
!req.headers["content-type"].startsWith(
|
||||
"multipart/form-data",
|
||||
)
|
||||
) {
|
||||
req.body = await req.urlencoded()
|
||||
req.body = await req.json(req.body)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (this.ctx.constructor.enableWebsockets === true) {
|
||||
this.ws = global.websocket = new rtengineng({
|
||||
path:
|
||||
this.ctx.constructor.wsPath ??
|
||||
`/${this.ctx.constructor.refName}`,
|
||||
onUpgrade: this.ctx.handleWsUpgrade,
|
||||
onConnection: this.ctx.handleWsConnection,
|
||||
onDisconnect: this.ctx.handleWsDisconnect,
|
||||
})
|
||||
|
||||
await this.ws.attach(this)
|
||||
}
|
||||
}
|
||||
|
||||
listen = async () => {
|
||||
await this.app.listen(this.ctx.constructor.listen_port)
|
||||
}
|
||||
|
||||
// close must be synchronous
|
||||
close = () => {
|
||||
if (this.ws && typeof this.ws.close === "function") {
|
||||
this.ws.close()
|
||||
}
|
||||
|
||||
if (typeof this.app.close === "function") {
|
||||
this.app.close()
|
||||
}
|
||||
|
||||
if (typeof this.ctx.onClose === "function") {
|
||||
this.ctx.onClose()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
import he from "hyper-express"
|
||||
import rtengine from "../../classes/rtengine"
|
||||
|
||||
export default class Engine {
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx
|
||||
}
|
||||
|
||||
app = null
|
||||
router = null
|
||||
ws = null
|
||||
|
||||
initialize = async () => {
|
||||
const serverParams = {
|
||||
max_body_length: 50 * 1024 * 1024, //50MB in bytes,
|
||||
}
|
||||
|
||||
if (this.ctx.ssl) {
|
||||
serverParams.key_file_name = this.ctx.ssl?.key ?? null
|
||||
serverParams.cert_file_name = this.ctx.ssl?.cert ?? null
|
||||
}
|
||||
|
||||
this.app = new he.Server(serverParams)
|
||||
|
||||
this.router = new he.Router()
|
||||
|
||||
// create a router map
|
||||
if (typeof this.router.map !== "object") {
|
||||
this.router.map = {}
|
||||
}
|
||||
|
||||
await this.router.any("*", (req, res) => {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
message: "Not found",
|
||||
})
|
||||
})
|
||||
|
||||
await this.app.use(async (req, res, next) => {
|
||||
if (req.method === "OPTIONS") {
|
||||
// handle cors
|
||||
if (this.ctx.constructor.ignoreCors) {
|
||||
res.setHeader("Access-Control-Allow-Methods", "*")
|
||||
res.setHeader("Access-Control-Allow-Origin", "*")
|
||||
res.setHeader("Access-Control-Allow-Headers", "*")
|
||||
}
|
||||
|
||||
return res.status(204).end()
|
||||
}
|
||||
|
||||
// register body parser
|
||||
if (req.headers["content-type"]) {
|
||||
if (
|
||||
!req.headers["content-type"].startsWith(
|
||||
"multipart/form-data",
|
||||
)
|
||||
) {
|
||||
req.body = await req.urlencoded()
|
||||
req.body = await req.json(req.body)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (this.ctx.constructor.enableWebsockets) {
|
||||
this.ws = global.websocket = new rtengine({
|
||||
requireAuth: this.ctx.constructor.requiredWsAuth,
|
||||
handleAuth: this.ctx.handleWsAuth,
|
||||
root: `/${this.ctx.constructor.refName}`,
|
||||
})
|
||||
|
||||
this.ws.initialize()
|
||||
|
||||
await this.ws.io.attachApp(this.app.uws_instance)
|
||||
}
|
||||
}
|
||||
|
||||
listen = async () => {
|
||||
await this.app.listen(this.ctx.constructor.listen_port)
|
||||
}
|
||||
|
||||
// close should be synchronous
|
||||
close = () => {
|
||||
if (this.ws) {
|
||||
this.ws.clear()
|
||||
|
||||
if (typeof this.ws?.close === "function") {
|
||||
this.ws.close()
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.app?.close === "function") {
|
||||
this.app.close()
|
||||
}
|
||||
|
||||
if (typeof this.ctx.onClose === "function") {
|
||||
this.ctx.onClose()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import HyperExpress from "./hyper-express"
|
||||
import HyperExpressNG from "./hyper-express-ng"
|
||||
import HeLegacy from "./he-legacy"
|
||||
import He from "./he"
|
||||
import Worker from "./worker"
|
||||
|
||||
export default {
|
||||
"hyper-express": HyperExpress,
|
||||
"hyper-express-ng": HyperExpressNG,
|
||||
"he-legacy": HeLegacy,
|
||||
he: He,
|
||||
worker: Worker,
|
||||
}
|
||||
|
@ -1,126 +1,118 @@
|
||||
import { EventEmitter } from "@foxify/events"
|
||||
|
||||
class WorkerEngineRouter {
|
||||
routes = []
|
||||
routes = []
|
||||
|
||||
get = (path, ...execs) => {
|
||||
get = (path, ...execs) => {}
|
||||
|
||||
}
|
||||
post = (path, ...execs) => {}
|
||||
|
||||
post = (path, ...execs) => {
|
||||
delete = (path, ...execs) => {}
|
||||
|
||||
}
|
||||
put = (path, ...execs) => {}
|
||||
|
||||
delete = (path, ...execs) => {
|
||||
patch = (path, ...execs) => {}
|
||||
|
||||
}
|
||||
head = (path, ...execs) => {}
|
||||
|
||||
put = (path, ...execs) => {
|
||||
options = (path, ...execs) => {}
|
||||
|
||||
}
|
||||
any = (path, ...execs) => {}
|
||||
|
||||
patch = (path, ...execs) => {
|
||||
|
||||
}
|
||||
|
||||
head = (path, ...execs) => {
|
||||
|
||||
}
|
||||
|
||||
options = (path, ...execs) => {
|
||||
|
||||
}
|
||||
|
||||
any = (path, ...execs) => {
|
||||
|
||||
}
|
||||
|
||||
use = (path, ...execs) => {
|
||||
|
||||
}
|
||||
use = (path, ...execs) => {}
|
||||
}
|
||||
|
||||
class WorkerEngine {
|
||||
static ipcPrefix = "rail:"
|
||||
static ipcPrefix = "rail:"
|
||||
|
||||
selfId = process.env.lb_service.id
|
||||
selfId = process.env.lb_service.id
|
||||
|
||||
router = new WorkerEngineRouter()
|
||||
router = new WorkerEngineRouter()
|
||||
|
||||
eventBus = new EventEmitter()
|
||||
eventBus = new EventEmitter()
|
||||
|
||||
perExecTail = []
|
||||
perExecTail = []
|
||||
|
||||
initialize = async () => {
|
||||
console.error(`[WorkerEngine] Worker engine its not implemented yet...`)
|
||||
initialize = async () => {
|
||||
console.error(`[WorkerEngine] Worker engine its not implemented yet...`)
|
||||
|
||||
process.on("message", this.handleIPCMessage)
|
||||
}
|
||||
process.on("message", this.handleIPCMessage)
|
||||
}
|
||||
|
||||
listen = async () => {
|
||||
console.log(`Sending Rail Register`)
|
||||
listen = async () => {
|
||||
console.log(`Sending Rail Register`)
|
||||
|
||||
process.send({
|
||||
type: "rail:register",
|
||||
data: {
|
||||
id: process.env.lb_service.id,
|
||||
pid: process.pid,
|
||||
routes: this.router.routes,
|
||||
}
|
||||
})
|
||||
}
|
||||
process.send({
|
||||
type: "rail:register",
|
||||
data: {
|
||||
id: process.env.lb_service.id,
|
||||
pid: process.pid,
|
||||
routes: this.router.routes,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
handleIPCMessage = async (msg) => {
|
||||
if (typeof msg !== "object") {
|
||||
// ignore, its not for us
|
||||
return false
|
||||
}
|
||||
handleIPCMessage = async (msg) => {
|
||||
if (typeof msg !== "object") {
|
||||
// ignore, its not for us
|
||||
return false
|
||||
}
|
||||
|
||||
if (!msg.event || !msg.event.startsWith(WorkerEngine.ipcPrefix)) {
|
||||
return false
|
||||
}
|
||||
if (!msg.event || !msg.event.startsWith(WorkerEngine.ipcPrefix)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const { event, payload } = msg
|
||||
const { event, payload } = msg
|
||||
|
||||
switch (event) {
|
||||
case "rail:request": {
|
||||
const { req } = payload
|
||||
switch (event) {
|
||||
case "rail:request": {
|
||||
const { req } = payload
|
||||
|
||||
break
|
||||
}
|
||||
case "rail:response": {
|
||||
break
|
||||
}
|
||||
case "rail:response": {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
use = (fn) => {
|
||||
if (fn instanceof WorkerEngineRouter) {
|
||||
this.router = fn
|
||||
return
|
||||
}
|
||||
|
||||
use = (fn) => {
|
||||
if (fn instanceof WorkerEngineRouter) {
|
||||
this.router = fn
|
||||
return
|
||||
}
|
||||
|
||||
if (fn instanceof Function) {
|
||||
this.perExecTail.push(fn)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (fn instanceof Function) {
|
||||
this.perExecTail.push(fn)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default class Engine {
|
||||
constructor(params) {
|
||||
this.params = params
|
||||
}
|
||||
constructor(params) {
|
||||
this.params = params
|
||||
}
|
||||
|
||||
app = new WorkerEngine()
|
||||
app = null
|
||||
router = new WorkerEngineRouter()
|
||||
map = new Map()
|
||||
|
||||
router = new WorkerEngineRouter()
|
||||
initialize = async () => {
|
||||
if (
|
||||
!process.env.lb_service ||
|
||||
process.env.lb_service?.type !== "worker"
|
||||
) {
|
||||
throw new Error(
|
||||
"No worker environment detected!\nThis engine is only meant to be used in a worker environment\n",
|
||||
)
|
||||
}
|
||||
|
||||
init = async () => {
|
||||
await this.app.initialize()
|
||||
}
|
||||
this.app = new WorkerEngine()
|
||||
|
||||
listen = async () => {
|
||||
await this.app.listen()
|
||||
}
|
||||
}
|
||||
await this.app.initialize()
|
||||
}
|
||||
|
||||
listen = async () => {
|
||||
await this.app.listen()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
Server: require("./server.js"),
|
||||
Endpoint: require("./classes/endpoint"),
|
||||
registerBaseAliases: require("./registerAliases"),
|
||||
version: require("../package.json").version,
|
||||
Server: require("./server"),
|
||||
Route: require("./classes/Route"),
|
||||
registerBaseAliases: require("./registerAliases"),
|
||||
version: require("../package.json").version,
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
export default async (server) => {
|
||||
const scanPath = path.join(__dirname, "../../", "baseEndpoints")
|
||||
const files = fs.readdirSync(scanPath)
|
||||
|
||||
for await (const file of files) {
|
||||
if (file === "index.js") {
|
||||
continue
|
||||
}
|
||||
|
||||
let endpoint = require(path.join(scanPath, file)).default
|
||||
|
||||
new endpoint(server)
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
import fs from "node:fs"
|
||||
|
||||
import Endpoint from "../../classes/endpoint"
|
||||
import RecursiveRegister from "../../lib/recursiveRegister"
|
||||
|
||||
const parametersRegex = /\[([a-zA-Z0-9_]+)\]/g
|
||||
|
||||
export default async (startDir, engine, server) => {
|
||||
if (!fs.existsSync(startDir)) {
|
||||
return engine
|
||||
}
|
||||
|
||||
await RecursiveRegister({
|
||||
start: startDir,
|
||||
match: async (filePath) => {
|
||||
return filePath.endsWith(".js") || filePath.endsWith(".ts")
|
||||
},
|
||||
onMatch: async ({ absolutePath, relativePath }) => {
|
||||
const paths = relativePath.split("/")
|
||||
|
||||
let method = paths[paths.length - 1].split(".")[0].toLocaleLowerCase()
|
||||
let route = paths.slice(0, paths.length - 1).join("/")
|
||||
|
||||
// parse parametrized routes
|
||||
route = route.replace(parametersRegex, ":$1")
|
||||
route = route.replace("[$]", "*")
|
||||
|
||||
// clean up
|
||||
route = route.replace(".js", "")
|
||||
route = route.replace(".ts", "")
|
||||
|
||||
// check if route ends with index
|
||||
if (route.endsWith("/index")) {
|
||||
route = route.replace("/index", "")
|
||||
}
|
||||
|
||||
// add leading slash
|
||||
route = `/${route}`
|
||||
|
||||
// import route
|
||||
let fn = require(absolutePath)
|
||||
|
||||
fn = fn.default ?? fn
|
||||
|
||||
if (typeof fn !== "function") {
|
||||
if (!fn.fn) {
|
||||
console.warn(`Missing fn handler in [${method}][${route}]`)
|
||||
return false
|
||||
}
|
||||
|
||||
if (Array.isArray(fn.useContext)) {
|
||||
let contexts = {}
|
||||
|
||||
for (const context of fn.useContext) {
|
||||
contexts[context] = server.contexts[context]
|
||||
}
|
||||
|
||||
fn.contexts = contexts
|
||||
|
||||
fn.fn.bind({ contexts })
|
||||
}
|
||||
}
|
||||
|
||||
new Endpoint(
|
||||
server,
|
||||
{
|
||||
route: route,
|
||||
enabled: true,
|
||||
middlewares: fn.middlewares,
|
||||
handlers: {
|
||||
[method]: fn.fn ?? fn,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return engine
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import fs from "node:fs"
|
||||
|
||||
import getRouteredFunctions from "../../utils/getRouteredFunctions"
|
||||
import flatRouteredFunctions from "../../utils/flatRouteredFunctions"
|
||||
|
||||
export default async (startDir, engine) => {
|
||||
if (!engine.ws || !fs.existsSync(startDir)) {
|
||||
return engine
|
||||
}
|
||||
|
||||
let events = await getRouteredFunctions(startDir)
|
||||
|
||||
events = flatRouteredFunctions(events)
|
||||
|
||||
if (typeof events !== "object") {
|
||||
return engine
|
||||
}
|
||||
|
||||
if (typeof engine.ws.registerEvents === "function") {
|
||||
await engine.ws.registerEvents(events)
|
||||
} else {
|
||||
for (const eventKey of Object.keys(events)) {
|
||||
engine.ws.events.set(eventKey, events[eventKey])
|
||||
}
|
||||
}
|
||||
|
||||
return engine
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
const { Controller } = require("../../classes/controller")
|
||||
const generateEndpointsFromDir = require("../generateEndpointsFromDir")
|
||||
|
||||
function generateControllerFromEndpointsDir(dir, controllerName) {
|
||||
const endpoints = generateEndpointsFromDir(dir)
|
||||
|
||||
return class extends Controller {
|
||||
static refName = controllerName
|
||||
|
||||
get = endpoints.get
|
||||
post = endpoints.post
|
||||
put = endpoints.put
|
||||
patch = endpoints.patch
|
||||
delete = endpoints.delete
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = generateControllerFromEndpointsDir
|
@ -1,23 +0,0 @@
|
||||
const loadEndpointsFromDir = require("../loadEndpointsFromDir")
|
||||
|
||||
function generateEndpointsFromDir(dir) {
|
||||
const loadedEndpoints = loadEndpointsFromDir(dir)
|
||||
|
||||
// filter by methods
|
||||
const endpointsByMethods = Object()
|
||||
|
||||
for (const endpointKey in loadedEndpoints) {
|
||||
const endpoint = loadedEndpoints[endpointKey]
|
||||
const method = endpoint.method.toLowerCase()
|
||||
|
||||
if (!endpointsByMethods[method]) {
|
||||
endpointsByMethods[method] = {}
|
||||
}
|
||||
|
||||
endpointsByMethods[method][endpoint.route] = loadedEndpoints[endpointKey]
|
||||
}
|
||||
|
||||
return endpointsByMethods
|
||||
}
|
||||
|
||||
module.exports = generateEndpointsFromDir
|
@ -1,41 +0,0 @@
|
||||
const fs = require("node:fs")
|
||||
const path = require("node:path")
|
||||
|
||||
function loadEndpointsFromDir(dir) {
|
||||
if (!dir) {
|
||||
throw new Error("No directory provided")
|
||||
}
|
||||
|
||||
if (!fs.existsSync(dir)) {
|
||||
throw new Error(`Directory [${dir}] does not exist`)
|
||||
}
|
||||
|
||||
// scan the directory for files
|
||||
const files = fs.readdirSync(dir)
|
||||
|
||||
// create an object to store the endpoints
|
||||
const endpoints = {}
|
||||
|
||||
// loop through the files
|
||||
for (const file of files) {
|
||||
// get the full path of the file
|
||||
const filePath = path.join(dir, file)
|
||||
|
||||
// get the file stats
|
||||
const stats = fs.statSync(filePath)
|
||||
|
||||
// if the file is a directory, recursively call this function
|
||||
if (stats.isDirectory()) {
|
||||
endpoints[file] = loadEndpointsFromDir(filePath)
|
||||
}
|
||||
|
||||
// if the file is a javascript file, require it and add it to the endpoints object
|
||||
if (stats.isFile() && path.extname(filePath) === ".js") {
|
||||
endpoints[path.basename(file, ".js")] = require(filePath).default
|
||||
}
|
||||
}
|
||||
|
||||
return endpoints
|
||||
}
|
||||
|
||||
module.exports = loadEndpointsFromDir
|
@ -1,8 +0,0 @@
|
||||
import cors from "cors"
|
||||
|
||||
export default cors({
|
||||
origin: "*",
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "CONNECT", "TRACE"],
|
||||
preflightContinue: false,
|
||||
optionsSuccessStatus: 204,
|
||||
})
|
@ -1,3 +1,15 @@
|
||||
import OperationError from "./classes/operation_error"
|
||||
import OperationError from "./classes/OperationError"
|
||||
import Endpoint from "./classes/Endpoint"
|
||||
import {
|
||||
Handler,
|
||||
HttpRequestHandler,
|
||||
MiddlewareHandler,
|
||||
WebsocketRequestHandler,
|
||||
} from "./classes/Handler"
|
||||
|
||||
global.OperationError = OperationError
|
||||
global.OperationError = OperationError
|
||||
global.Endpoint = Endpoint
|
||||
global.Handler = Handler
|
||||
global.HttpRequestHandler = HttpRequestHandler
|
||||
global.MiddlewareHandler = MiddlewareHandler
|
||||
global.WebsocketRequestHandler = WebsocketRequestHandler
|
||||
|
22
server/src/registers/baseHeaders.js
Normal file
22
server/src/registers/baseHeaders.js
Normal file
@ -0,0 +1,22 @@
|
||||
import Vars from "../vars"
|
||||
|
||||
export default (server) => {
|
||||
if (!server || !server.headers || !server.engine || !server.engine.app) {
|
||||
return null
|
||||
}
|
||||
|
||||
let headers = {
|
||||
...server.headers,
|
||||
...Vars.baseHeaders,
|
||||
}
|
||||
|
||||
headers = Object.entries(headers)
|
||||
|
||||
server.engine.app.use((req, res, next) => {
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
res.setHeader(headers[i][0], headers[i][1])
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
}
|
14
server/src/registers/baseMiddlewares.js
Normal file
14
server/src/registers/baseMiddlewares.js
Normal file
@ -0,0 +1,14 @@
|
||||
import composeMiddlewares from "../utils/composeMiddlewares"
|
||||
import Vars from "../vars"
|
||||
|
||||
export default async (server) => {
|
||||
const middlewares = composeMiddlewares(
|
||||
{ ...server.middlewares, ...Vars.baseMiddlewares },
|
||||
server.params.useMiddlewares,
|
||||
"/*",
|
||||
)
|
||||
|
||||
middlewares.forEach((middleware) => {
|
||||
server.engine.app.use(middleware)
|
||||
})
|
||||
}
|
21
server/src/registers/baseRoutes.js
Executable file
21
server/src/registers/baseRoutes.js
Executable file
@ -0,0 +1,21 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
import Vars from "../vars"
|
||||
|
||||
export default async (server) => {
|
||||
const scanPath = path.resolve(Vars.libPath, "baseRoutes")
|
||||
const files = fs.readdirSync(scanPath)
|
||||
|
||||
for await (const file of files) {
|
||||
if (file === "index.js") {
|
||||
continue
|
||||
}
|
||||
|
||||
let RouteModule = await import(path.join(scanPath, file))
|
||||
|
||||
RouteModule = RouteModule.default
|
||||
|
||||
new RouteModule(server).register()
|
||||
}
|
||||
}
|
6
server/src/registers/bypassCorsHeaders.js
Normal file
6
server/src/registers/bypassCorsHeaders.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default (server) => {
|
||||
server.headers["Access-Control-Allow-Origin"] = "*"
|
||||
server.headers["Access-Control-Allow-Methods"] = "*"
|
||||
server.headers["Access-Control-Allow-Headers"] = "*"
|
||||
server.headers["Access-Control-Allow-Credentials"] = "true"
|
||||
}
|
64
server/src/registers/httpFileRoutes.js
Executable file
64
server/src/registers/httpFileRoutes.js
Executable file
@ -0,0 +1,64 @@
|
||||
import fs from "node:fs"
|
||||
|
||||
import Route from "../classes/Route"
|
||||
import RecursiveRegister from "../utils/recursiveRegister"
|
||||
|
||||
const parametersRegex = /\[([a-zA-Z0-9_]+)\]/g
|
||||
|
||||
export default async (startDir, server) => {
|
||||
if (!fs.existsSync(startDir)) {
|
||||
return null
|
||||
}
|
||||
|
||||
await RecursiveRegister({
|
||||
start: startDir,
|
||||
match: async (filePath) => {
|
||||
return filePath.endsWith(".js") || filePath.endsWith(".ts")
|
||||
},
|
||||
onMatch: async ({ absolutePath, relativePath }) => {
|
||||
const paths = relativePath.split("/")
|
||||
|
||||
let method = paths[paths.length - 1]
|
||||
.split(".")[0]
|
||||
.toLocaleLowerCase()
|
||||
let route = paths.slice(0, paths.length - 1).join("/")
|
||||
|
||||
// parse parametrized routes
|
||||
route = route.replace(parametersRegex, ":$1")
|
||||
route = route.replace("[$]", "*")
|
||||
|
||||
// clean up
|
||||
route = route.replace(".js", "")
|
||||
route = route.replace(".ts", "")
|
||||
|
||||
// check if route ends with index
|
||||
if (route.endsWith("/index")) {
|
||||
route = route.replace("/index", "")
|
||||
}
|
||||
|
||||
// add leading slash
|
||||
route = `/${route}`
|
||||
|
||||
// import endpoint
|
||||
let fileObj = await import(absolutePath)
|
||||
|
||||
fileObj = fileObj.default ?? fileObj
|
||||
|
||||
if (typeof fileObj !== "function") {
|
||||
if (typeof fileObj.fn !== "function") {
|
||||
console.warn(`Missing fn handler in [${method}][${route}]`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
new Route(server, {
|
||||
route: route,
|
||||
useMiddlewares: fileObj.useMiddlewares,
|
||||
useContexts: fileObj.useContexts,
|
||||
handlers: {
|
||||
[method]: fileObj.fn ?? fileObj,
|
||||
},
|
||||
}).register()
|
||||
},
|
||||
})
|
||||
}
|
47
server/src/registers/ipcService.js
Normal file
47
server/src/registers/ipcService.js
Normal file
@ -0,0 +1,47 @@
|
||||
export default async (server) => {
|
||||
if (!process.env.lb_service || !process.send) {
|
||||
console.error("IPC not available")
|
||||
return null
|
||||
}
|
||||
|
||||
// get only the root paths
|
||||
let paths = Array.from(server.engine.map.keys()).map((key) => {
|
||||
const root = key.split("/")[1]
|
||||
|
||||
return "/" + root
|
||||
})
|
||||
|
||||
// remove duplicates
|
||||
paths = [...new Set(paths)]
|
||||
|
||||
// remove "" and _map
|
||||
paths = paths.filter((key) => {
|
||||
if (key === "/" || key === "/_map") {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
process.send({
|
||||
type: "service:register",
|
||||
id: process.env.lb_service.id,
|
||||
index: process.env.lb_service.index,
|
||||
register: {
|
||||
namespace: server.params.refName,
|
||||
http: {
|
||||
enabled: true,
|
||||
paths: paths,
|
||||
proto: server.hasSSL ? "https" : "http",
|
||||
},
|
||||
websocket: {
|
||||
enabled: server.params.websockets,
|
||||
path: server.params.refName ?? `/${server.params.refName}`,
|
||||
},
|
||||
listen: {
|
||||
ip: server.params.listenIp,
|
||||
port: server.params.listenPort,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
28
server/src/registers/websocketFileEvents.js
Executable file
28
server/src/registers/websocketFileEvents.js
Executable file
@ -0,0 +1,28 @@
|
||||
import fs from "node:fs"
|
||||
|
||||
import getRouteredFunctions from "../utils/getRouteredFunctions"
|
||||
import flatRouteredFunctions from "../utils/flatRouteredFunctions"
|
||||
|
||||
export default async (startDir, server) => {
|
||||
if (!server.engine.ws || !fs.existsSync(startDir)) {
|
||||
return null
|
||||
}
|
||||
|
||||
let events = await getRouteredFunctions(startDir)
|
||||
|
||||
events = flatRouteredFunctions(events)
|
||||
|
||||
if (typeof events !== "object") {
|
||||
return null
|
||||
}
|
||||
|
||||
if (typeof server.engine.ws.registerEvents === "function") {
|
||||
await server.engine.ws.registerEvents(events)
|
||||
} else {
|
||||
for (const eventKey of Object.keys(events)) {
|
||||
server.engine.ws.events.set(eventKey, events[eventKey])
|
||||
}
|
||||
}
|
||||
|
||||
return server
|
||||
}
|
@ -1,136 +1,126 @@
|
||||
import("./patches")
|
||||
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
import { EventEmitter } from "@foxify/events"
|
||||
|
||||
import defaults from "./defaults"
|
||||
|
||||
import IPCClient from "./classes/IPCClient"
|
||||
import Endpoint from "./classes/endpoint"
|
||||
import Route from "./classes/Route"
|
||||
|
||||
import registerBaseEndpoints from "./initializators/registerBaseEndpoints"
|
||||
import registerWebsocketsEvents from "./initializators/registerWebsocketsEvents"
|
||||
import registerHttpRoutes from "./initializators/registerHttpRoutes"
|
||||
import registerBaseRoutes from "./registers/baseRoutes"
|
||||
import registerBaseMiddlewares from "./registers/baseMiddlewares"
|
||||
import registerBaseHeaders from "./registers/baseHeaders"
|
||||
import registerWebsocketsFileEvents from "./registers/websocketFileEvents"
|
||||
import registerHttpFileRoutes from "./registers/httpFileRoutes"
|
||||
import registerServiceToIPC from "./registers/ipcService"
|
||||
import bypassCorsHeaders from "./registers/bypassCorsHeaders"
|
||||
|
||||
import isExperimental from "./utils/isExperimental"
|
||||
import getHostAddress from "./utils/getHostAddress"
|
||||
import composeMiddlewares from "./utils/composeMiddlewares"
|
||||
|
||||
import Vars from "./vars"
|
||||
import Engines from "./engines"
|
||||
|
||||
class Server {
|
||||
constructor(params = {}, controllers = {}, middlewares = {}, headers = {}) {
|
||||
this.isExperimental = defaults.isExperimental ?? false
|
||||
|
||||
constructor(params = {}) {
|
||||
if (this.isExperimental) {
|
||||
console.warn("\n🚧 This version of Linebridge is experimental! 🚧")
|
||||
console.warn(`Version: ${defaults.version}\n`)
|
||||
console.warn(`Version: ${Vars.libPkg.version}\n`)
|
||||
}
|
||||
|
||||
this.params = {
|
||||
...defaults.params,
|
||||
...(params.default ?? params),
|
||||
...Vars.defaultParams,
|
||||
...params,
|
||||
}
|
||||
|
||||
this.controllers = {
|
||||
...(controllers.default ?? controllers),
|
||||
// overrides some params with constructor values
|
||||
if (typeof this.constructor.refName === "string") {
|
||||
this.params.refName = this.constructor.refName
|
||||
}
|
||||
|
||||
this.middlewares = {
|
||||
...(middlewares.default ?? middlewares),
|
||||
if (typeof this.constructor.useEngine === "string") {
|
||||
this.params.useEngine = this.constructor.useEngine
|
||||
}
|
||||
|
||||
this.headers = {
|
||||
...defaults.headers,
|
||||
...(headers.default ?? headers),
|
||||
if (typeof this.constructor.listenIp === "string") {
|
||||
this.params.listenIp = this.constructor.listenIp
|
||||
}
|
||||
|
||||
// fix and fulfill params
|
||||
this.params.useMiddlewares = this.params.useMiddlewares ?? []
|
||||
if (
|
||||
typeof this.constructor.listenPort === "string" ||
|
||||
typeof this.constructor.listenPort === "number"
|
||||
) {
|
||||
this.params.listenPort = this.constructor.listenPort
|
||||
}
|
||||
|
||||
this.params.name = this.constructor.refName ?? this.params.refName
|
||||
if (typeof this.constructor.websockets === "boolean") {
|
||||
this.params.websockets = this.constructor.websockets
|
||||
}
|
||||
|
||||
this.params.useEngine =
|
||||
this.constructor.useEngine ??
|
||||
this.params.useEngine ??
|
||||
"hyper-express"
|
||||
if (typeof this.constructor.bypassCors === "boolean") {
|
||||
this.params.bypassCors = this.constructor.bypassCors
|
||||
}
|
||||
|
||||
this.params.listen_ip =
|
||||
this.constructor.listenIp ??
|
||||
this.constructor.listen_ip ??
|
||||
this.params.listen_ip ??
|
||||
"0.0.0.0"
|
||||
if (typeof this.constructor.baseRoutes === "boolean") {
|
||||
this.params.baseRoutes = this.constructor.baseRoutes
|
||||
}
|
||||
|
||||
this.params.listen_port =
|
||||
this.constructor.listenPort ??
|
||||
this.constructor.listen_port ??
|
||||
this.params.listen_port ??
|
||||
3000
|
||||
if (typeof this.constructor.routesPath === "string") {
|
||||
this.params.routesPath = this.constructor.routesPath
|
||||
}
|
||||
|
||||
this.params.http_protocol = this.params.http_protocol ?? "http"
|
||||
if (typeof this.constructor.wsRoutesPath === "string") {
|
||||
this.params.wsRoutesPath = this.constructor.wsRoutesPath
|
||||
}
|
||||
|
||||
this.params.http_address = `${this.params.http_protocol}://${defaults.localhost_address}:${this.params.listen_port}`
|
||||
if (typeof this.constructor.useMiddlewares !== "undefined") {
|
||||
if (!Array.isArray(this.constructor.useMiddlewares)) {
|
||||
this.constructor.useMiddlewares = [
|
||||
this.constructor.useMiddlewares,
|
||||
]
|
||||
}
|
||||
|
||||
this.params.enableWebsockets =
|
||||
this.constructor.enableWebsockets ??
|
||||
this.params.enableWebsockets ??
|
||||
false
|
||||
this.params.useMiddlewares = this.constructor.useMiddlewares
|
||||
}
|
||||
|
||||
this.params.ignoreCors =
|
||||
this.constructor.ignoreCors ?? this.params.ignoreCors ?? true
|
||||
|
||||
this.params.disableBaseEndpoints =
|
||||
this.constructor.disableBaseEndpoints ??
|
||||
this.params.disableBaseEndpoints ??
|
||||
false
|
||||
|
||||
this.params.routesPath =
|
||||
this.constructor.routesPath ??
|
||||
this.params.routesPath ??
|
||||
path.resolve(process.cwd(), "routes")
|
||||
|
||||
this.params.wsRoutesPath =
|
||||
this.constructor.wsRoutesPath ??
|
||||
this.params.wsRoutesPath ??
|
||||
path.resolve(process.cwd(), "routes_ws")
|
||||
|
||||
globalThis._linebridge = {
|
||||
name: this.params.name,
|
||||
useEngine: this.params.useEngine,
|
||||
listenIp: this.params.listen_ip,
|
||||
listenPort: this.params.listen_port,
|
||||
httpProtocol: this.params.http_protocol,
|
||||
httpAddress: this.params.http_address,
|
||||
enableWebsockets: this.params.enableWebsockets,
|
||||
ignoreCors: this.params.ignoreCors,
|
||||
routesPath: this.params.routesPath,
|
||||
validHttpMethods: defaults.valid_http_methods,
|
||||
global._linebridge = {
|
||||
vars: Vars,
|
||||
params: this.params,
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
eventBus = new EventEmitter()
|
||||
middlewares = {}
|
||||
headers = {}
|
||||
events = {}
|
||||
contexts = {}
|
||||
|
||||
engine = null
|
||||
|
||||
events = null
|
||||
get hasSSL() {
|
||||
if (!this.ssl) {
|
||||
return false
|
||||
}
|
||||
|
||||
ipc = null
|
||||
return this.ssl.key && this.ssl.cert
|
||||
}
|
||||
|
||||
ipcEvents = null
|
||||
|
||||
eventBus = new EventEmitter()
|
||||
get isExperimental() {
|
||||
return isExperimental()
|
||||
}
|
||||
|
||||
initialize = async () => {
|
||||
const startHrTime = process.hrtime()
|
||||
|
||||
// register events
|
||||
if (this.events) {
|
||||
if (this.events.default) {
|
||||
this.events = this.events.default
|
||||
}
|
||||
// resolve current local private address of the host
|
||||
this.localAddress = getHostAddress()
|
||||
|
||||
for (const [eventName, eventHandler] of Object.entries(
|
||||
this.events,
|
||||
)) {
|
||||
this.eventBus.on(eventName, eventHandler)
|
||||
}
|
||||
this.contexts["server"] = this
|
||||
|
||||
// register declared events to eventBus
|
||||
for (const [eventName, eventHandler] of Object.entries(this.events)) {
|
||||
this.eventBus.on(eventName, eventHandler)
|
||||
}
|
||||
|
||||
// initialize engine
|
||||
@ -140,28 +130,19 @@ class Server {
|
||||
throw new Error(`Engine ${this.params.useEngine} not found`)
|
||||
}
|
||||
|
||||
// construct engine instance
|
||||
// important, pass this instance to the engine constructor
|
||||
this.engine = new this.engine(this)
|
||||
|
||||
// fire engine initialization
|
||||
if (typeof this.engine.initialize === "function") {
|
||||
await this.engine.initialize()
|
||||
}
|
||||
|
||||
// check if ws events are defined
|
||||
if (typeof this.wsEvents !== "undefined") {
|
||||
if (!this.engine.ws) {
|
||||
console.warn(
|
||||
"`wsEvents` detected, but Websockets are not enabled! Ignoring...",
|
||||
)
|
||||
} else {
|
||||
for (const [eventName, eventHandler] of Object.entries(
|
||||
this.wsEvents,
|
||||
)) {
|
||||
this.engine.ws.events.set(eventName, eventHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
// at this point, we wanna to pass to onInitialize hook,
|
||||
// a simple base context, without any registers extra
|
||||
|
||||
// try to execute onInitialize hook
|
||||
// fire onInitialize hook
|
||||
if (typeof this.onInitialize === "function") {
|
||||
try {
|
||||
await this.onInitialize()
|
||||
@ -171,211 +152,123 @@ class Server {
|
||||
}
|
||||
}
|
||||
|
||||
// set defaults
|
||||
this.useDefaultHeaders()
|
||||
this.useDefaultMiddlewares()
|
||||
// Now gonna initialize the final steps & registers
|
||||
|
||||
if (this.routes) {
|
||||
// bypassCors if needed
|
||||
if (this.params.bypassCors) {
|
||||
bypassCorsHeaders(this)
|
||||
}
|
||||
|
||||
// register base headers & middlewares
|
||||
registerBaseHeaders(this)
|
||||
registerBaseMiddlewares(this)
|
||||
|
||||
// if websocket enabled, lets do some work
|
||||
if (typeof this.engine.ws === "object") {
|
||||
// register declared ws events
|
||||
if (typeof this.wsEvents === "object") {
|
||||
for (const [eventName, eventHandler] of Object.entries(
|
||||
this.wsEvents,
|
||||
)) {
|
||||
this.engine.ws.events.set(eventName, eventHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now, initialize declared routes with Endpoint class
|
||||
if (typeof this.routes === "object") {
|
||||
for (const [route, endpoint] of Object.entries(this.routes)) {
|
||||
this.engine.router.map[route] = new Endpoint(this, {
|
||||
new Route(this, {
|
||||
...endpoint,
|
||||
route: route,
|
||||
handlers: {
|
||||
[endpoint.method.toLowerCase()]: endpoint.fn,
|
||||
},
|
||||
})
|
||||
}).register()
|
||||
}
|
||||
}
|
||||
|
||||
// register http & ws routes
|
||||
this.engine = await registerHttpRoutes(
|
||||
this.params.routesPath,
|
||||
this.engine,
|
||||
this,
|
||||
)
|
||||
this.engine = await registerWebsocketsEvents(
|
||||
this.params.wsRoutesPath,
|
||||
this.engine,
|
||||
)
|
||||
// register http file routes
|
||||
await registerHttpFileRoutes(this.params.routesPath, this)
|
||||
|
||||
// register base endpoints if enabled
|
||||
if (!this.params.disableBaseEndpoints) {
|
||||
await registerBaseEndpoints(this)
|
||||
// register ws file routes
|
||||
await registerWebsocketsFileEvents(this.params.wsRoutesPath, this)
|
||||
|
||||
// register base routes if enabled
|
||||
if (this.params.baseRoutes == true) {
|
||||
await registerBaseRoutes(this)
|
||||
}
|
||||
|
||||
// use main router
|
||||
await this.engine.app.use(this.engine.router)
|
||||
|
||||
// if is a linebridge service then initialize IPC Channels
|
||||
// if is a linebridge service, then initialize IPC Channels
|
||||
if (process.env.lb_service) {
|
||||
await this.initializeIpc()
|
||||
await this.registerServiceToIPC()
|
||||
}
|
||||
console.info("🚄 Starting IPC client")
|
||||
this.ipc = global.ipc = new IPCClient(this, process)
|
||||
|
||||
// try to execute beforeInitialize hook.
|
||||
if (typeof this.afterInitialize === "function") {
|
||||
await this.afterInitialize()
|
||||
await registerServiceToIPC(this)
|
||||
}
|
||||
|
||||
// listen
|
||||
await this.engine.listen()
|
||||
|
||||
// execute afterInitialize hook.
|
||||
if (typeof this.afterInitialize === "function") {
|
||||
await this.afterInitialize()
|
||||
}
|
||||
|
||||
// calculate elapsed time on ms, to fixed 2
|
||||
const elapsedHrTime = process.hrtime(startHrTime)
|
||||
const elapsedTimeInMs = elapsedHrTime[0] * 1e3 + elapsedHrTime[1] / 1e6
|
||||
|
||||
console.info(
|
||||
`🛰 Server ready!\n\t - ${this.params.http_protocol}://${this.params.listen_ip}:${this.params.listen_port} \n\t - Tooks ${elapsedTimeInMs.toFixed(2)}ms \n\t - Websocket: ${this.engine.ws ? "Enabled" : "Disabled"}`,
|
||||
`🛰 Server ready!\n\t - ${this.hasSSL ? "https" : "http"}://${this.params.listenIp}:${this.params.listenPort} \n\t - Websocket: ${this.engine.ws ? "Enabled" : "Disabled"} \n\t - Routes: ${this.engine.map.size} \n\t - Tooks: ${elapsedTimeInMs.toFixed(2)}ms \n\t `,
|
||||
)
|
||||
}
|
||||
|
||||
initializeIpc = async () => {
|
||||
console.info("🚄 Starting IPC client")
|
||||
|
||||
this.ipc = global.ipc = new IPCClient(this, process)
|
||||
}
|
||||
|
||||
useDefaultHeaders = () => {
|
||||
this.engine.app.use((req, res, next) => {
|
||||
Object.keys(this.headers).forEach((key) => {
|
||||
res.setHeader(key, this.headers[key])
|
||||
})
|
||||
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
useDefaultMiddlewares = async () => {
|
||||
const middlewares = await this.resolveMiddlewares([
|
||||
...this.params.useMiddlewares,
|
||||
...(this.useMiddlewares ?? []),
|
||||
...defaults.useMiddlewares,
|
||||
])
|
||||
|
||||
middlewares.forEach((middleware) => {
|
||||
this.engine.app.use(middleware)
|
||||
})
|
||||
}
|
||||
|
||||
register = {
|
||||
http: (endpoint, ..._middlewares) => {
|
||||
http: (obj) => {
|
||||
// check and fix method
|
||||
endpoint.method = endpoint.method?.toLowerCase() ?? "get"
|
||||
obj.method = obj.method?.toLowerCase() ?? "get"
|
||||
|
||||
if (defaults.fixed_http_methods[endpoint.method]) {
|
||||
endpoint.method = defaults.fixed_http_methods[endpoint.method]
|
||||
if (Vars.fixedHttpMethods[obj.method]) {
|
||||
obj.method = Vars.fixedHttpMethods[obj.method]
|
||||
}
|
||||
|
||||
// check if method is supported
|
||||
if (typeof this.engine.router[endpoint.method] !== "function") {
|
||||
throw new Error(`Method [${endpoint.method}] is not supported!`)
|
||||
if (typeof this.engine.router[obj.method] !== "function") {
|
||||
throw new Error(`Method [${obj.method}] is not supported!`)
|
||||
}
|
||||
|
||||
// grab the middlewares
|
||||
let middlewares = [..._middlewares]
|
||||
// compose the middlewares
|
||||
obj.middlewares = composeMiddlewares(
|
||||
{ ...this.middlewares, ...Vars.baseMiddlewares },
|
||||
obj.middlewares,
|
||||
`[${obj.method.toUpperCase()}] ${obj.route}`,
|
||||
)
|
||||
|
||||
if (endpoint.middlewares) {
|
||||
if (!Array.isArray(endpoint.middlewares)) {
|
||||
endpoint.middlewares = [endpoint.middlewares]
|
||||
}
|
||||
|
||||
middlewares = [
|
||||
...middlewares,
|
||||
...this.resolveMiddlewares(endpoint.middlewares),
|
||||
]
|
||||
}
|
||||
|
||||
this.engine.router.map[endpoint.route] = {
|
||||
method: endpoint.method,
|
||||
path: endpoint.route,
|
||||
}
|
||||
// set to the endpoints map, used by _map
|
||||
this.engine.map.set(obj.route, {
|
||||
method: obj.method,
|
||||
path: obj.route,
|
||||
})
|
||||
|
||||
// register endpoint to http interface router
|
||||
this.engine.router[endpoint.method](
|
||||
endpoint.route,
|
||||
...middlewares,
|
||||
endpoint.fn,
|
||||
this.engine.router[obj.method](
|
||||
obj.route,
|
||||
...obj.middlewares,
|
||||
obj.fn,
|
||||
)
|
||||
},
|
||||
ws: (wsEndpointObj) => {},
|
||||
}
|
||||
|
||||
resolveMiddlewares = (requestedMiddlewares) => {
|
||||
const middlewares = {
|
||||
...this.middlewares,
|
||||
...defaults.middlewares,
|
||||
_fireClose = () => {
|
||||
if (typeof this.onClose === "function") {
|
||||
this.onClose()
|
||||
}
|
||||
|
||||
if (typeof requestedMiddlewares === "string") {
|
||||
requestedMiddlewares = [requestedMiddlewares]
|
||||
if (typeof this.engine.close === "function") {
|
||||
this.engine.close()
|
||||
}
|
||||
|
||||
const execs = []
|
||||
|
||||
requestedMiddlewares.forEach((middlewareKey) => {
|
||||
if (typeof middlewareKey === "string") {
|
||||
if (typeof middlewares[middlewareKey] !== "function") {
|
||||
throw new Error(`Middleware ${middlewareKey} not found!`)
|
||||
}
|
||||
|
||||
execs.push(middlewares[middlewareKey])
|
||||
}
|
||||
|
||||
if (typeof middlewareKey === "function") {
|
||||
execs.push(middlewareKey)
|
||||
}
|
||||
})
|
||||
|
||||
return execs
|
||||
}
|
||||
|
||||
registerServiceToIPC = () => {
|
||||
if (!process.env.lb_service || !process.send) {
|
||||
console.error("IPC not available")
|
||||
return null
|
||||
}
|
||||
|
||||
// get only the root paths
|
||||
let paths = Object.keys(this.engine.router.map).map((key) => {
|
||||
const root = key.split("/")[1]
|
||||
|
||||
return "/" + root
|
||||
})
|
||||
|
||||
// remove duplicates
|
||||
paths = [...new Set(paths)]
|
||||
|
||||
// remove "" and _map
|
||||
paths = paths.filter((key) => {
|
||||
if (key === "/" || key === "/_map") {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
process.send({
|
||||
type: "service:register",
|
||||
id: process.env.lb_service.id,
|
||||
index: process.env.lb_service.index,
|
||||
register: {
|
||||
namespace: this.constructor.refName,
|
||||
http: {
|
||||
enabled: true,
|
||||
paths: paths,
|
||||
proto: this.ssl?.key && this.ssl?.cert ? "https" : "http",
|
||||
},
|
||||
websocket: {
|
||||
enabled: this.constructor.enableWebsockets,
|
||||
path:
|
||||
this.constructor.wsPath ??
|
||||
`/${this.constructor.refName}`,
|
||||
},
|
||||
listen: {
|
||||
ip: this.params.listen_ip,
|
||||
port: this.params.listen_port,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
29
server/src/utils/composeMiddlewares.js
Normal file
29
server/src/utils/composeMiddlewares.js
Normal file
@ -0,0 +1,29 @@
|
||||
export default (middlewares, selectors, endpointRef) => {
|
||||
if (!middlewares || !selectors) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (typeof selectors === "string") {
|
||||
selectors = [selectors]
|
||||
}
|
||||
|
||||
const execs = []
|
||||
|
||||
selectors.forEach((middlewareKey) => {
|
||||
if (typeof middlewareKey === "string") {
|
||||
if (typeof middlewares[middlewareKey] !== "function") {
|
||||
throw new Error(
|
||||
`Required middleware [${middlewareKey}] not found!\n\t- Required by endpoint > ${endpointRef}\n\n`,
|
||||
)
|
||||
}
|
||||
|
||||
execs.push(middlewares[middlewareKey])
|
||||
}
|
||||
|
||||
if (typeof middlewareKey === "function") {
|
||||
execs.push(middlewareKey)
|
||||
}
|
||||
})
|
||||
|
||||
return execs
|
||||
}
|
23
server/src/utils/getHostAddress.js
Normal file
23
server/src/utils/getHostAddress.js
Normal file
@ -0,0 +1,23 @@
|
||||
import os from "node:os"
|
||||
|
||||
export default function getHostAddress() {
|
||||
const interfaces = os.networkInterfaces()
|
||||
|
||||
for (const key in interfaces) {
|
||||
const iface = interfaces[key]
|
||||
|
||||
for (let index = 0; index < iface.length; index++) {
|
||||
const alias = iface[index]
|
||||
|
||||
if (
|
||||
alias.family === "IPv4" &&
|
||||
alias.address !== "127.0.0.1" &&
|
||||
!alias.internal
|
||||
) {
|
||||
return alias.address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "0.0.0.0"
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default as Schematized } from "./schematized"
|
8
server/src/utils/isExperimental.js
Normal file
8
server/src/utils/isExperimental.js
Normal file
@ -0,0 +1,8 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
import Vars from "../vars"
|
||||
|
||||
export default () => {
|
||||
return fs.existsSync(path.resolve(Vars.rootLibPath, ".experimental"))
|
||||
}
|
48
server/src/vars.js
Executable file
48
server/src/vars.js
Executable file
@ -0,0 +1,48 @@
|
||||
import path from "node:path"
|
||||
|
||||
const rootLibPath = path.resolve(__dirname, "../")
|
||||
const packageJSON = require(rootLibPath, "../package.json")
|
||||
const projectPkg = require(path.resolve(process.cwd(), "package.json"))
|
||||
|
||||
export default {
|
||||
libPath: __dirname,
|
||||
rootLibPath: rootLibPath,
|
||||
libPkg: packageJSON,
|
||||
projectCwd: process.cwd(),
|
||||
projectPkg: projectPkg,
|
||||
defaultParams: {
|
||||
refName: "linebridge",
|
||||
listenIp: "0.0.0.0",
|
||||
listenPort: 3000,
|
||||
useEngine: "he",
|
||||
websockets: false,
|
||||
bypassCors: false,
|
||||
baseRoutes: true,
|
||||
routesPath: path.resolve(process.cwd(), "routes"),
|
||||
wsRoutesPath: path.resolve(process.cwd(), "ws_routes"),
|
||||
useMiddlewares: [],
|
||||
httpMethods: [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"patch",
|
||||
"del",
|
||||
"delete",
|
||||
"trace",
|
||||
"head",
|
||||
"any",
|
||||
"options",
|
||||
"ws",
|
||||
],
|
||||
},
|
||||
baseHeaders: {
|
||||
server: "linebridge",
|
||||
"lb-version": packageJSON.version,
|
||||
},
|
||||
baseMiddlewares: {
|
||||
logs: require("./middlewares/logger").default,
|
||||
},
|
||||
fixedHttpMethods: {
|
||||
del: "delete",
|
||||
},
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user