新增socketio-admin依赖,koa静态资源映射,启动成功后自动启动admin-ui
This commit is contained in:
parent
7900fce2da
commit
d176a09d7c
|
|
@ -17,6 +17,13 @@ const sticky_1 = require("@socket.io/sticky");
|
||||||
const redis_adapter_1 = require("@socket.io/redis-adapter");
|
const redis_adapter_1 = require("@socket.io/redis-adapter");
|
||||||
const ioredis_1 = tslib_1.__importDefault(require("ioredis"));
|
const ioredis_1 = tslib_1.__importDefault(require("ioredis"));
|
||||||
const socket_io_1 = require("socket.io");
|
const socket_io_1 = require("socket.io");
|
||||||
|
const admin_ui_1 = require("@socket.io/admin-ui");
|
||||||
|
const koa_static_1 = tslib_1.__importDefault(require("koa-static"));
|
||||||
|
const koa_mount_1 = tslib_1.__importDefault(require("koa-mount"));
|
||||||
|
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
||||||
|
const utils_1 = require("../utils");
|
||||||
|
const bcryptjs_1 = tslib_1.__importDefault(require("bcryptjs"));
|
||||||
|
const socketAdminUI = (0, path_1.join)(__dirname, '../../ui/socket-admin');
|
||||||
const DATA_SUBSCRIBE_NAMESPACE = '/dsn';
|
const DATA_SUBSCRIBE_NAMESPACE = '/dsn';
|
||||||
const SOCKET_NAMESPACE = '/sn';
|
const SOCKET_NAMESPACE = '/sn';
|
||||||
const SERVER_SUBSCRIBER_NAMESPACE = process.env.OAK_SSUB_NAMESPACE || '/ssub';
|
const SERVER_SUBSCRIBER_NAMESPACE = process.env.OAK_SSUB_NAMESPACE || '/ssub';
|
||||||
|
|
@ -46,8 +53,9 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
||||||
koa.use((0, koa_logger_1.default)());
|
koa.use((0, koa_logger_1.default)());
|
||||||
// socket
|
// socket
|
||||||
const httpServer = (0, http_1.createServer)(koa.callback());
|
const httpServer = (0, http_1.createServer)(koa.callback());
|
||||||
|
const socketPath = connector.getSocketPath();
|
||||||
const socketOption = {
|
const socketOption = {
|
||||||
path: connector.getSocketPath(),
|
path: socketPath,
|
||||||
cors: ['development', 'staging'].includes(process.env.NODE_ENV)
|
cors: ['development', 'staging'].includes(process.env.NODE_ENV)
|
||||||
? {
|
? {
|
||||||
origin: '*',
|
origin: '*',
|
||||||
|
|
@ -130,6 +138,16 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
||||||
else {
|
else {
|
||||||
console.log('以单实例模式启动');
|
console.log('以单实例模式启动');
|
||||||
}
|
}
|
||||||
|
// 密码使用随机字符串
|
||||||
|
const passwordForAdminUI = process.env.SOCKET_ADMIN_PASSWORD || (0, utils_1.randomString)(16);
|
||||||
|
(0, admin_ui_1.instrument)(io, {
|
||||||
|
auth: {
|
||||||
|
type: "basic", // 使用基本认证,生产建议关闭或换成自定义 auth
|
||||||
|
username: "admin",
|
||||||
|
password: bcryptjs_1.default.hashSync(passwordForAdminUI, 10), // 使用 bcrypt 加密密码
|
||||||
|
},
|
||||||
|
mode: "development", // 或 "production"
|
||||||
|
});
|
||||||
const appLoader = clusterInfo.usingCluster
|
const appLoader = clusterInfo.usingCluster
|
||||||
? new oak_backend_base_1.ClusterAppLoader(path, io.of(DATA_SUBSCRIBE_NAMESPACE), io.of(SOCKET_NAMESPACE), io.of(SERVER_SUBSCRIBER_NAMESPACE), connector.getSocketPath())
|
? new oak_backend_base_1.ClusterAppLoader(path, io.of(DATA_SUBSCRIBE_NAMESPACE), io.of(SOCKET_NAMESPACE), io.of(SERVER_SUBSCRIBER_NAMESPACE), connector.getSocketPath())
|
||||||
: new oak_backend_base_1.AppLoader(path, io.of(DATA_SUBSCRIBE_NAMESPACE), io.of(SOCKET_NAMESPACE), io.of(SERVER_SUBSCRIBER_NAMESPACE));
|
: new oak_backend_base_1.AppLoader(path, io.of(DATA_SUBSCRIBE_NAMESPACE), io.of(SOCKET_NAMESPACE), io.of(SERVER_SUBSCRIBER_NAMESPACE));
|
||||||
|
|
@ -238,7 +256,6 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
||||||
else {
|
else {
|
||||||
url += `:${port}`;
|
url += `:${port}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example:
|
// Example:
|
||||||
// import { io } from "socket.io-client";
|
// import { io } from "socket.io-client";
|
||||||
// const socket = io('https://example.com/order', {
|
// const socket = io('https://example.com/order', {
|
||||||
|
|
@ -293,13 +310,26 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
||||||
router.get(connector.getEndpointRouter(), async (ctx) => {
|
router.get(connector.getEndpointRouter(), async (ctx) => {
|
||||||
ctx.response.body = endpoints;
|
ctx.response.body = endpoints;
|
||||||
});
|
});
|
||||||
|
// 注册静态资源
|
||||||
|
koa.use((0, koa_mount_1.default)('/socket-admin', (0, koa_static_1.default)(socketAdminUI)));
|
||||||
koa.use(router.routes());
|
koa.use(router.routes());
|
||||||
koa.on('error', (err) => {
|
koa.on('error', (err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
httpServer.listen(serverConfiguration.port, () => {
|
httpServer.listen(serverConfiguration.port, () => {
|
||||||
console.log(`server will listen on port ${serverConfiguration.port}`);
|
const protocol = nginx?.ssl ? 'https' : 'http';
|
||||||
|
const host = hostname || 'localhost';
|
||||||
|
const port = nginx?.port || (clusterInfo.usingCluster ? (process.env.PM2_PORT || 8080) : serverConfiguration.port);
|
||||||
|
const baseUrl = `${protocol}://${host}:${port}`;
|
||||||
|
const adminUIUrl = `${baseUrl}/socket-admin`;
|
||||||
|
console.log(chalk_1.default.greenBright.bold('\n🚀 Server started successfully!\n'));
|
||||||
|
console.log(`🔗 ${chalk_1.default.cyan('Server URL')}: ${chalk_1.default.underline(baseUrl)}`);
|
||||||
|
// socketio地址
|
||||||
|
console.log(`🔗 ${chalk_1.default.cyan('Socket URL')}: ${chalk_1.default.underline(concat(url, socketPath))}`);
|
||||||
|
console.log(`🛠️ ${chalk_1.default.magenta('Socket Admin UI')}: ${chalk_1.default.underline(adminUIUrl)}`);
|
||||||
|
// 账号密码
|
||||||
|
console.log(`🔑 ${chalk_1.default.yellow('Socket Admin UI Password')}: ${chalk_1.default.red(passwordForAdminUI)}\n`);
|
||||||
});
|
});
|
||||||
if (!omitWatchers) {
|
if (!omitWatchers) {
|
||||||
appLoader.startWatchers();
|
appLoader.startWatchers();
|
||||||
|
|
|
||||||
|
|
@ -57,3 +57,10 @@ export declare function formatJsonByFile<T extends Object>(data: T): string;
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export declare function deWeight(arr: Array<any> | Set<any>, type: any): Set<any>;
|
export declare function deWeight(arr: Array<any> | Set<any>, type: any): Set<any>;
|
||||||
|
/**
|
||||||
|
* @name 随机字符串
|
||||||
|
* @export
|
||||||
|
* @param {number} length
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export declare function randomString(length: number): string;
|
||||||
|
|
|
||||||
16
lib/utils.js
16
lib/utils.js
|
|
@ -7,6 +7,7 @@ exports.intersect = intersect;
|
||||||
exports.union = union;
|
exports.union = union;
|
||||||
exports.formatJsonByFile = formatJsonByFile;
|
exports.formatJsonByFile = formatJsonByFile;
|
||||||
exports.deWeight = deWeight;
|
exports.deWeight = deWeight;
|
||||||
|
exports.randomString = randomString;
|
||||||
/**
|
/**
|
||||||
* @name 从一组路径里查找到所有json文件
|
* @name 从一组路径里查找到所有json文件
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -96,3 +97,18 @@ function deWeight(arr, type) {
|
||||||
}
|
}
|
||||||
return new Set([...map.values()]);
|
return new Set([...map.values()]);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @name 随机字符串
|
||||||
|
* @export
|
||||||
|
* @param {number} length
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function randomString(length) {
|
||||||
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * chars.length);
|
||||||
|
result += chars[randomIndex];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
||||||
11
package.json
11
package.json
|
|
@ -19,7 +19,8 @@
|
||||||
"scripts/**/*",
|
"scripts/**/*",
|
||||||
"template/**/*",
|
"template/**/*",
|
||||||
"templateExample/**/*",
|
"templateExample/**/*",
|
||||||
"templateFiles/**/*"
|
"templateFiles/**/*",
|
||||||
|
"ui/**/*"
|
||||||
],
|
],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "",
|
"license": "",
|
||||||
|
|
@ -33,7 +34,9 @@
|
||||||
"@types/inquirer": "^9.0.3",
|
"@types/inquirer": "^9.0.3",
|
||||||
"@types/koa": "^2.15.0",
|
"@types/koa": "^2.15.0",
|
||||||
"@types/koa-logger": "^3.1.5",
|
"@types/koa-logger": "^3.1.5",
|
||||||
|
"@types/koa-mount": "^4.0.5",
|
||||||
"@types/koa-router": "^7.4.8",
|
"@types/koa-router": "^7.4.8",
|
||||||
|
"@types/koa-static": "^4.0.4",
|
||||||
"@types/lodash": "^4.17.13",
|
"@types/lodash": "^4.17.13",
|
||||||
"@types/node": "^20.6.0",
|
"@types/node": "^20.6.0",
|
||||||
"@types/shelljs": "^0.8.8",
|
"@types/shelljs": "^0.8.8",
|
||||||
|
|
@ -46,6 +49,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
|
||||||
|
"@socket.io/admin-ui": "^0.5.1",
|
||||||
"@socket.io/cluster-adapter": "^0.2.2",
|
"@socket.io/cluster-adapter": "^0.2.2",
|
||||||
"@socket.io/redis-adapter": "^8.3.0",
|
"@socket.io/redis-adapter": "^8.3.0",
|
||||||
"@socket.io/sticky": "^1.0.4",
|
"@socket.io/sticky": "^1.0.4",
|
||||||
|
|
@ -56,6 +60,7 @@
|
||||||
"babel-loader": "^8.2.3",
|
"babel-loader": "^8.2.3",
|
||||||
"babel-plugin-named-asset-import": "^0.3.8",
|
"babel-plugin-named-asset-import": "^0.3.8",
|
||||||
"babel-preset-react-app": "^10.0.1",
|
"babel-preset-react-app": "^10.0.1",
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
"bfj": "^7.0.2",
|
"bfj": "^7.0.2",
|
||||||
"browser-assert": "^1.2.1",
|
"browser-assert": "^1.2.1",
|
||||||
"browserify-sign": "4.2.2",
|
"browserify-sign": "4.2.2",
|
||||||
|
|
@ -64,7 +69,7 @@
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"camelcase": "^6.2.1",
|
"camelcase": "^6.2.1",
|
||||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.2",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"commander": "^6.0.0",
|
"commander": "^6.0.0",
|
||||||
"compression-webpack-plugin": "^10.0.0",
|
"compression-webpack-plugin": "^10.0.0",
|
||||||
|
|
@ -98,7 +103,9 @@
|
||||||
"koa": "^2.13.4",
|
"koa": "^2.13.4",
|
||||||
"koa-body": "^5.0.0",
|
"koa-body": "^5.0.0",
|
||||||
"koa-logger": "^3.2.1",
|
"koa-logger": "^3.2.1",
|
||||||
|
"koa-mount": "^4.2.0",
|
||||||
"koa-router": "^11.0.1",
|
"koa-router": "^11.0.1",
|
||||||
|
"koa-static": "^5.0.0",
|
||||||
"less": "^4.1.2",
|
"less": "^4.1.2",
|
||||||
"less-loader": "^10.2.0",
|
"less-loader": "^10.2.0",
|
||||||
"loader-utils": "^3.2.0",
|
"loader-utils": "^3.2.0",
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,14 @@ import { createAdapter as createRedisAdapter } from "@socket.io/redis-adapter";
|
||||||
import Redis from "ioredis";
|
import Redis from "ioredis";
|
||||||
import { Server, ServerOptions } from "socket.io";
|
import { Server, ServerOptions } from "socket.io";
|
||||||
import { ServerConfiguration } from 'oak-domain/lib/types/Configuration';
|
import { ServerConfiguration } from 'oak-domain/lib/types/Configuration';
|
||||||
|
import { instrument } from "@socket.io/admin-ui";
|
||||||
|
import serve from 'koa-static';
|
||||||
|
import mount from 'koa-mount';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import { randomString } from '../utils';
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
|
|
||||||
|
const socketAdminUI = join(__dirname, '../../ui/socket-admin');
|
||||||
|
|
||||||
const DATA_SUBSCRIBE_NAMESPACE = '/dsn';
|
const DATA_SUBSCRIBE_NAMESPACE = '/dsn';
|
||||||
const SOCKET_NAMESPACE = '/sn';
|
const SOCKET_NAMESPACE = '/sn';
|
||||||
|
|
@ -65,8 +73,9 @@ export async function startup<ED extends EntityDict & BaseEntityDict, FrontCxt e
|
||||||
koa.use(logger());
|
koa.use(logger());
|
||||||
// socket
|
// socket
|
||||||
const httpServer = createServer(koa.callback());
|
const httpServer = createServer(koa.callback());
|
||||||
|
const socketPath = connector.getSocketPath();
|
||||||
const socketOption: Partial<ServerOptions> = {
|
const socketOption: Partial<ServerOptions> = {
|
||||||
path: connector.getSocketPath(),
|
path: socketPath,
|
||||||
cors: ['development', 'staging'].includes(process.env.NODE_ENV!)
|
cors: ['development', 'staging'].includes(process.env.NODE_ENV!)
|
||||||
? {
|
? {
|
||||||
origin: '*',
|
origin: '*',
|
||||||
|
|
@ -153,6 +162,18 @@ export async function startup<ED extends EntityDict & BaseEntityDict, FrontCxt e
|
||||||
console.log('以单实例模式启动');
|
console.log('以单实例模式启动');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 密码使用随机字符串
|
||||||
|
const passwordForAdminUI = process.env.SOCKET_ADMIN_PASSWORD || randomString(16);
|
||||||
|
|
||||||
|
instrument(io, {
|
||||||
|
auth: {
|
||||||
|
type: "basic", // 使用基本认证,生产建议关闭或换成自定义 auth
|
||||||
|
username: "admin",
|
||||||
|
password: bcrypt.hashSync(passwordForAdminUI, 10), // 使用 bcrypt 加密密码
|
||||||
|
},
|
||||||
|
mode: "development", // 或 "production"
|
||||||
|
});
|
||||||
|
|
||||||
const appLoader = clusterInfo.usingCluster
|
const appLoader = clusterInfo.usingCluster
|
||||||
? new ClusterAppLoader<ED, Cxt>(
|
? new ClusterAppLoader<ED, Cxt>(
|
||||||
path,
|
path,
|
||||||
|
|
@ -368,14 +389,30 @@ export async function startup<ED extends EntityDict & BaseEntityDict, FrontCxt e
|
||||||
ctx.response.body = endpoints;
|
ctx.response.body = endpoints;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 注册静态资源
|
||||||
|
koa.use(mount('/socket-admin', serve(socketAdminUI)));
|
||||||
|
|
||||||
koa.use(router.routes());
|
koa.use(router.routes());
|
||||||
|
|
||||||
koa.on('error', (err) => {
|
koa.on('error', (err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
httpServer.listen(serverConfiguration.port, () => {
|
httpServer.listen(serverConfiguration.port, () => {
|
||||||
console.log(`server will listen on port ${serverConfiguration.port}`);
|
const protocol = nginx?.ssl ? 'https' : 'http';
|
||||||
|
const host = hostname || 'localhost';
|
||||||
|
const port = nginx?.port || (clusterInfo.usingCluster ? (process.env.PM2_PORT || 8080) : serverConfiguration.port);
|
||||||
|
const baseUrl = `${protocol}://${host}:${port}`;
|
||||||
|
const adminUIUrl = `${baseUrl}/socket-admin`;
|
||||||
|
|
||||||
|
console.log(chalk.greenBright.bold('\n🚀 Server started successfully!\n'));
|
||||||
|
console.log(`🔗 ${chalk.cyan('Server URL')}: ${chalk.underline(baseUrl)}`);
|
||||||
|
// socketio地址
|
||||||
|
console.log(`🔗 ${chalk.cyan('Socket URL')}: ${chalk.underline(concat(url, socketPath))}`);
|
||||||
|
console.log(`🛠️ ${chalk.magenta('Socket Admin UI')}: ${chalk.underline(adminUIUrl)}`);
|
||||||
|
// 账号密码
|
||||||
|
console.log(`🔑 ${chalk.yellow('Socket Admin UI Password')}: ${chalk.red(passwordForAdminUI)}\n`);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!omitWatchers) {
|
if (!omitWatchers) {
|
||||||
|
|
|
||||||
16
src/utils.ts
16
src/utils.ts
|
|
@ -96,3 +96,19 @@ export function deWeight(arr: Array<any> | Set<any>, type: any) {
|
||||||
}
|
}
|
||||||
return new Set([...map.values()]);
|
return new Set([...map.values()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name 随机字符串
|
||||||
|
* @export
|
||||||
|
* @param {number} length
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function randomString(length: number): string {
|
||||||
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * chars.length);
|
||||||
|
result += chars[randomIndex];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue