Compare commits

..

15 Commits

Author SHA1 Message Date
392f01d509 v0.8.3 2020-07-02 17:08:20 +08:00
75b892e1c0 feat: enable compilerOptions importHelpers
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-02 16:57:16 +08:00
c2d825eed0 feat: update typescript version
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-02 11:23:14 +08:00
393e63c017 v0.8.2 2020-07-01 14:41:21 +08:00
46d6748f31 feat: update dep version
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-01 14:33:16 +08:00
b6803ed01c feat: update plugins
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-30 16:02:51 +08:00
bbfc70fdd6 feat: complate @ccms/web package
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-30 16:02:39 +08:00
e28af3fbbc feat: optimize process & plugin loader and scanner
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-30 15:58:51 +08:00
14eef46e67 fix: boardcast error when socket send packet
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-30 14:02:18 +08:00
1c4f512c89 v0.8.1 2020-06-23 16:15:17 +08:00
a8ae60625b feat: update config & package.json
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-23 16:07:55 +08:00
e7bc14294e feat: update script
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-23 15:53:24 +08:00
4750f31b6e feat: optimize plugin scan & load
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-23 15:51:55 +08:00
18df821049 v0.8.0 2020-06-22 18:58:05 +08:00
ef98c49e1d feat: use @javatypes defiend java type
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-06-22 10:44:17 +08:00
70 changed files with 1095 additions and 603 deletions

View File

@@ -21,3 +21,4 @@ wallaby.js
.theia .theia
type_definitions type_definitions
tsconfig.tsbuildinfo tsconfig.tsbuildinfo
*.tsbuildinfo

View File

@@ -1,5 +1,5 @@
{ {
"version": "0.7.0", "version": "0.8.3",
"useWorkspaces": true, "useWorkspaces": true,
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
@@ -10,6 +10,7 @@
"stream": true "stream": true
}, },
"publish": { "publish": {
"access": "public",
"registry": "https://repo.yumc.pw/repository/npm-hosted/" "registry": "https://repo.yumc.pw/repository/npm-hosted/"
} }
} }

View File

@@ -12,14 +12,14 @@
"build": "lerna run build --scope=\"@ccms/!(plugins)\"", "build": "lerna run build --scope=\"@ccms/!(plugins)\"",
"build:plugins": "lerna run build --scope=\"@ccms/plugins\"", "build:plugins": "lerna run build --scope=\"@ccms/plugins\"",
"ug": "yarn upgrade-interactive --latest", "ug": "yarn upgrade-interactive --latest",
"np": "lerna exec \"npm publish --registry https://registry.npmjs.org\" --scope=\"@ccms/!(client|plugins)\"", "np": "lerna exec \"npm publish --access=public --registry https://registry.npmjs.org\" --scope=\"@ccms/!(client|plugins)\"",
"lsp": "npm login --registry=https://registry.npmjs.org --scope=@ccms", "lsp": "npm login --registry=https://registry.npmjs.org --scope=@ccms",
"lp": "lerna publish" "lp": "lerna publish --registry https://registry.npmjs.org"
}, },
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
], ],
"devDependencies": { "devDependencies": {
"lerna": "^3.21.0" "lerna": "^3.22.1"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/amqp", "name": "@ccms/amqp",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript amqp package", "description": "MiaoScript amqp package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,14 +19,17 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/nashorn": "^0.7.0", "@ccms/common": "^0.8.3",
"@ccms/common": "^0.7.0", "@ccms/container": "^0.8.3"
"@ccms/container": "^0.7.0"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.8.3",
"@javatypes/amqp-client": "^0.0.2",
"@javatypes/spring-amqp": "^0.0.2",
"@javatypes/spring-rabbit": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
} }
} }

View File

@@ -1,9 +1,12 @@
import { ConnectionFactoryAdapter } from "./connection" import { ConnectionFactoryAdapter } from "./connection"
import { RabbitTemplateAdapter } from "./template" import { RabbitTemplateAdapter } from "./template"
import { JSClass } from "@ccms/container"
export const RabbitAdmin = Java.type('org.springframework.amqp.rabbit.core.RabbitAdmin') export const RabbitAdmin = Java.type('org.springframework.amqp.rabbit.core.RabbitAdmin')
export class RabbitAdminAdapter { export class RabbitAdminAdapter {
@JSClass('org.springframework.amqp.rabbit.core.RabbitAdmin')
private RabbitAdmin: org.springframework.amqp.rabbit.core.RabbitAdmin
private _Handler: org.springframework.amqp.rabbit.core.RabbitAdmin private _Handler: org.springframework.amqp.rabbit.core.RabbitAdmin
constructor(config: RabbitTemplateAdapter | org.springframework.amqp.rabbit.core.RabbitTemplate | ConnectionFactoryAdapter | org.springframework.amqp.rabbit.connection.ConnectionFactory) { constructor(config: RabbitTemplateAdapter | org.springframework.amqp.rabbit.core.RabbitTemplate | ConnectionFactoryAdapter | org.springframework.amqp.rabbit.connection.ConnectionFactory) {
this._Handler = new RabbitAdmin((config instanceof RabbitTemplateAdapter || config instanceof ConnectionFactoryAdapter) ? config.getHandler() : config) this._Handler = new RabbitAdmin((config instanceof RabbitTemplateAdapter || config instanceof ConnectionFactoryAdapter) ? config.getHandler() : config)

View File

@@ -2,7 +2,7 @@ import { ConnectionFactoryAdapter, ConnectionFactory } from "./connection"
import { ChannelAwareMessageListenerAdapter, ChannelAwareMessageListener } from "./listener" import { ChannelAwareMessageListenerAdapter, ChannelAwareMessageListener } from "./listener"
export const SimpleMessageListenerContainer = org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer export const SimpleMessageListenerContainer = org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer
export const AcknowledgeMode = org.springframework.amqp.core.AcknowledgeMode export const AcknowledgeMode: org.springframework.amqp.core.AcknowledgeMode = Java.type('org.springframework.amqp.core.AcknowledgeMode')
interface ContainerConfig { interface ContainerConfig {
connectionFactory: ConnectionFactoryAdapter | typeof ConnectionFactory connectionFactory: ConnectionFactoryAdapter | typeof ConnectionFactory
queueNames: string[] queueNames: string[]

View File

@@ -1,8 +1,6 @@
import { JSClass } from "@ccms/container" import { JSClass } from "@ccms/container"
import { RabbitAdminAdapter, MessageListenerContainerAdapter, AcknowledgeMode, MessageHandler, ChannelAwareMessageListenerAdapter } from "./adapter" import { RabbitAdminAdapter, MessageListenerContainerAdapter, AcknowledgeMode, MessageHandler, ChannelAwareMessageListenerAdapter } from "./adapter"
const RabbitAdmin = Java.type('org.springframework.amqp.rabbit.core.RabbitAdmin')
export class AmqpAdmin { export class AmqpAdmin {
@JSClass('org.springframework.amqp.core.TopicExchange') @JSClass('org.springframework.amqp.core.TopicExchange')
private TopicExchange: typeof org.springframework.amqp.core.TopicExchange private TopicExchange: typeof org.springframework.amqp.core.TopicExchange
@@ -10,14 +8,15 @@ export class AmqpAdmin {
private Queue: typeof org.springframework.amqp.core.Queue private Queue: typeof org.springframework.amqp.core.Queue
@JSClass('org.springframework.amqp.core.Binding') @JSClass('org.springframework.amqp.core.Binding')
private Binding: typeof org.springframework.amqp.core.Binding private Binding: typeof org.springframework.amqp.core.Binding
@JSClass('org.springframework.amqp.rabbit.core.RabbitAdmin')
private RabbitAdmin: typeof org.springframework.amqp.rabbit.core.RabbitAdmin
@JSClass('org.springframework.amqp.core.Binding.DestinationType') @JSClass('org.springframework.amqp.core.Binding.DestinationType')
private DestinationType: typeof org.springframework.amqp.core.Binding.DestinationType private DestinationType: org.springframework.amqp.core.Binding.DestinationType
private amqpAdmin: org.springframework.amqp.rabbit.core.RabbitAdmin private amqpAdmin: org.springframework.amqp.rabbit.core.RabbitAdmin
constructor(amqpAdmin: org.springframework.amqp.rabbit.core.RabbitAdmin | any) { constructor(amqpAdmin: org.springframework.amqp.rabbit.core.RabbitAdmin | any) {
if (amqpAdmin instanceof RabbitAdmin) { if (amqpAdmin instanceof this.RabbitAdmin) {
this.amqpAdmin = amqpAdmin this.amqpAdmin = amqpAdmin
} else if (amqpAdmin instanceof RabbitAdminAdapter) { } else if (amqpAdmin instanceof RabbitAdminAdapter) {
this.amqpAdmin = amqpAdmin.getHandler() this.amqpAdmin = amqpAdmin.getHandler()

View File

@@ -1,8 +1,8 @@
/// <reference types="@ccms/nashorn" /> /// <reference types="@ccms/nashorn" />
/// <reference types="@ccms/types/dist/typings/jdk" /> /// <reference types="@javatypes/jdk" />
/// <reference types="@ccms/types/dist/typings/spring" /> /// <reference types="@javatypes/spring-amqp" />
/// <reference types="@ccms/types/dist/typings/spring/amqp" /> /// <reference types="@javatypes/spring-rabbit" />
/// <reference types="@ccms/types/dist/typings/rabbitmq" /> /// <reference types="@javatypes/amqp-client" />
import { amqp } from '@ccms/api' import { amqp } from '@ccms/api'
import { getContainer, reduceMetadata } from '@ccms/container' import { getContainer, reduceMetadata } from '@ccms/container'

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/api", "name": "@ccms/api",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript api package", "description": "MiaoScript api package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,16 +19,16 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"@ccms/container": "^0.7.0", "@ccms/common": "^0.8.3",
"@ccms/ployfill": "^0.7.0", "@ccms/container": "^0.8.3",
"@ccms/common": "^0.7.0", "@ccms/ployfill": "^0.8.3",
"base64-js": "^1.3.1", "base64-js": "^1.3.1",
"source-map-builder": "^0.0.7" "source-map-builder": "^0.0.7"
}, },
"devDependencies": { "devDependencies": {
"@types/base64-js": "^1.2.5", "@types/base64-js": "^1.3.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
} }
} }

View File

@@ -30,6 +30,44 @@ export namespace plugin {
getPlugins(): Map<string, plugin.Plugin> getPlugins(): Map<string, plugin.Plugin>
} }
export const PluginScanner = Symbol("PluginScanner") export const PluginScanner = Symbol("PluginScanner")
/**
* 插件加载元信息
*/
export interface PluginLoadMetadata {
/**
* 插件加载类型
*/
type: string
/**
* 插件名称
*/
name?: string
/**
* 插件文件
*/
file?: string
/**
* 插件实例
*/
instance?: any
/**
* 插件元信息
*/
metadata?: PluginMetadata
/**
* 插件扫描器
*/
scanner: PluginScanner
/**
* 插件加载器
*/
loader?: PluginLoader
/**
* 是否已加载
*/
loaded?: boolean
[key: string]: any
}
/** /**
* 插件扫描器 * 插件扫描器
*/ */
@@ -39,15 +77,20 @@ export namespace plugin {
*/ */
type: string type: string
/** /**
* 扫描插件 返回插件加载列表 * 扫描插件目录 返回插件加载元信息列表
* @param target 扫描目标 * @param target 扫描目标
*/ */
scan(target: any): string[] scan(target: any): PluginLoadMetadata[]
/**
* 读取插件 返回插件加载元信息
* @param target
*/
read(target: any): PluginLoadMetadata
/** /**
* 加载扫描到的目标 * 加载扫描到的目标
* @param target 加载目标 * @param target 加载目标
*/ */
load(target: any): any load(target: PluginLoadMetadata): PluginLoadMetadata
} }
export const PluginLoader = Symbol("PluginLoader") export const PluginLoader = Symbol("PluginLoader")
/** /**
@@ -63,12 +106,12 @@ export namespace plugin {
* @param target 加载目标 * @param target 加载目标
* @param result 扫描器加载的结果 * @param result 扫描器加载的结果
*/ */
require(target: any, result: any): PluginMetadata require(loadMetadata: PluginLoadMetadata): PluginLoadMetadata
/** /**
* 构建插件 * 构建插件
* @param metadata 插件元信息 * @param metadata 插件元信息
*/ */
build(metadata: any): Plugin build(metadata: PluginMetadata): Plugin
/** /**
* Load 阶段 * Load 阶段
* @param plugin 插件 * @param plugin 插件
@@ -133,13 +176,9 @@ export namespace plugin {
*/ */
type?: string type?: string
/** /**
* 插件扫描器 * 插件加载信息
*/ */
scanner?: PluginScanner loadMetadata?: PluginLoadMetadata
/**
* 插件加载器
*/
loader?: PluginLoader
/** /**
* 插件本体 * 插件本体
*/ */

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/bukkit", "name": "@ccms/bukkit",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript bukkit package", "description": "MiaoScript bukkit package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,13 +19,14 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@javatypes/spigot-api": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/common": "^0.7.0", "@ccms/common": "^0.8.3",
"@ccms/container": "^0.7.0" "@ccms/container": "^0.8.3"
} }
} }

View File

@@ -1,4 +1,4 @@
/// <reference types="@ccms/types/dist/typings/bukkit" /> /// <reference types="@javatypes/spigot-api" />
import { server } from '@ccms/api' import { server } from '@ccms/api'
import { Container } from '@ccms/container' import { Container } from '@ccms/container'

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/bungee", "name": "@ccms/bungee",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript bungee package", "description": "MiaoScript bungee package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,13 +19,14 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@javatypes/bungee-api": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/common": "^0.7.0", "@ccms/common": "^0.8.3",
"@ccms/container": "^0.7.0" "@ccms/container": "^0.8.3"
} }
} }

View File

@@ -1,4 +1,4 @@
/// <reference types="@ccms/types/dist/typings/bungee" /> /// <reference types="@javatypes/bungee-api" />
import { server } from '@ccms/api' import { server } from '@ccms/api'
import { Container } from '@ccms/container' import { Container } from '@ccms/container'

View File

@@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "@ccms/client", "name": "@ccms/client",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript client package", "description": "MiaoScript client package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -22,10 +22,10 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"minecraft-protocol": "^1.11.0" "minecraft-protocol": "^1.13.0"
}, },
"devDependencies": { "devDependencies": {
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/common", "name": "@ccms/common",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript api package", "description": "MiaoScript api package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,12 +19,11 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.8.3",
"@javatypes/jdk": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
},
"dependencies": {
"@ccms/nashorn": "^0.7.0"
}, },
"gitHead": "562e2d00175c9d3a99c8b672aa07e6d92706a027" "gitHead": "562e2d00175c9d3a99c8b672aa07e6d92706a027"
} }

View File

@@ -1,4 +1,4 @@
/// <reference types="@ccms/types/dist/typings/jdk" /> /// <reference types="@javatypes/jdk" />
/** /**
* 反射工具类 * 反射工具类
@@ -11,7 +11,7 @@ const methodCache = []
class Reflect { class Reflect {
private obj: java.lang.Object private obj: java.lang.Object
private class: java.lang.Class private class: java.lang.Class<any>
constructor(obj: any) { constructor(obj: any) {
// if (obj === undefined || obj === null) { throw Error(`reflect object can't be ${obj}!`) } // if (obj === undefined || obj === null) { throw Error(`reflect object can't be ${obj}!`) }
@@ -26,8 +26,8 @@ class Reflect {
} }
} }
method(name: string, ...args: any[]) { method(name: string, ...args: java.lang.Class<any>[]) {
return declaredMethod(this.class, name, args) return declaredMethod(this.class, name, ...args)
} }
methods() { methods() {
@@ -90,7 +90,7 @@ function types(values: any[], def?: any) {
if (values === null) { if (values === null) {
return [] return []
} }
let result: java.lang.Class[] = [] let result: java.lang.Class<any>[] = []
values.forEach(t => result.push((t || def) ? JavaObject.class : t instanceof JavaClass ? t : t.class)) values.forEach(t => result.push((t || def) ? JavaObject.class : t instanceof JavaClass ? t : t.class))
return result return result
} }
@@ -119,7 +119,7 @@ function declaredConstructor(clazz, param) {
return accessible(constructor) return accessible(constructor)
} }
function declaredField(clazz: java.lang.Class, name: string | java.lang.String) { function declaredField(clazz: java.lang.Class<any>, name: string | java.lang.String) {
if (!clazz) { throw Error(`target class can't be ${clazz}!`) } if (!clazz) { throw Error(`target class can't be ${clazz}!`) }
let target = clazz let target = clazz
let field = null let field = null
@@ -139,17 +139,17 @@ function declaredField(clazz: java.lang.Class, name: string | java.lang.String)
return field return field
} }
function declaredMethod(clazz: java.lang.Class, name: string, ...clazzs: any[]): java.lang.reflect.Method { function declaredMethod(clazz: java.lang.Class<any>, name: string, ...clazzs: java.lang.Class<any>[]): java.lang.reflect.Method {
let key = clazz.getName() + '.' + name + ':' + (clazzs || []).join(':') let key = clazz.getName() + '.' + name + ':' + (clazzs || []).join(':')
if (!methodCache[key]) { if (!methodCache[key]) {
try { try {
methodCache[key] = clazz.getMethod(name, clazzs as any) methodCache[key] = clazz.getMethod(name, ...clazzs)
} catch (ex) { } catch (ex) {
try { try {
methodCache[key] = clazz.getDeclaredMethod(name, clazzs as any) methodCache[key] = clazz.getDeclaredMethod(name, clazzs as any)
} catch (ex) { } catch (ex) {
for (const m of Java.from(declaredMethods(clazz))) { for (const m of Java.from(declaredMethods(clazz))) {
if (m.name == name) { if (m.getName() == name) {
methodCache[key] = m methodCache[key] = m
break break
} }
@@ -160,8 +160,8 @@ function declaredMethod(clazz: java.lang.Class, name: string, ...clazzs: any[]):
return methodCache[key] return methodCache[key]
} }
function declaredMethods(clazz) { function declaredMethods(clazz: java.lang.Class<any>) {
return clazz.declaredMethods return clazz.getDeclaredMethods()
} }
let classMethodsCache: any[] = [] let classMethodsCache: any[] = []

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/compile", "name": "@ccms/compile",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript compile package", "description": "MiaoScript compile package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -21,6 +21,6 @@
"devDependencies": { "devDependencies": {
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/container", "name": "@ccms/container",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript container package", "description": "MiaoScript container package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,13 +19,13 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.8.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"inversify": "^5.0.1", "inversify": "^5.0.1",
"inversify-binding-decorators": "^4.0.0", "inversify-binding-decorators": "^4.0.0"
"@ccms/nashorn": "^0.7.0"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/core", "name": "@ccms/core",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript api package", "description": "MiaoScript api package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -21,11 +21,11 @@
"devDependencies": { "devDependencies": {
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/container": "^0.7.0" "@ccms/container": "^0.8.3"
}, },
"gitHead": "781524f83e52cad26d7c480513e3c525df867121" "gitHead": "781524f83e52cad26d7c480513e3c525df867121"
} }

View File

@@ -53,7 +53,6 @@ class MiaoScriptCore {
console.i18n("ms.core.engine.disable") console.i18n("ms.core.engine.disable")
this.pluginManager.disable(this.pluginManager.getPlugins()) this.pluginManager.disable(this.pluginManager.getPlugins())
this.taskManager.disable() this.taskManager.disable()
process.emit('exit', 0)
process.exit(0) process.exit(0)
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/database", "name": "@ccms/database",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript database package", "description": "MiaoScript database package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,12 +19,13 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@javatypes/spring-jdbc": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/container": "^0.7.0" "@ccms/container": "^0.8.3"
} }
} }

View File

@@ -1,5 +1,3 @@
/// <reference types="@ccms/types/dist/typings/spring" />
import { Model } from './model' import { Model } from './model'
const HikariDataSource = Java.type('com.zaxxer.hikari.HikariDataSource') const HikariDataSource = Java.type('com.zaxxer.hikari.HikariDataSource')
@@ -69,7 +67,7 @@ export class DataBase {
*/ */
query<T>(sql: string, ...args: any[]): Array<T> { query<T>(sql: string, ...args: any[]): Array<T> {
let startTime = Date.now() let startTime = Date.now()
let result = Java.from(this.jdbcTemplate.queryForList(sql, args)) let result = Java.from<any>(this.jdbcTemplate.queryForList(sql, args))
console.debug(java.lang.String.format(`\n[DB] query \nSQL : ${sql.replace(/\?/ig, '%s')} \nCOST : ${Date.now() - startTime}ms`, args)) console.debug(java.lang.String.format(`\n[DB] query \nSQL : ${sql.replace(/\?/ig, '%s')} \nCOST : ${Date.now() - startTime}ms`, args))
return result return result
} }

View File

@@ -1,5 +1,6 @@
/// <reference types="@ccms/nashorn" /> /// <reference types="@ccms/nashorn" />
/// <reference types="@ccms/types/dist/typings/jdk/index" /> /// <reference types="@javatypes/jdk" />
/// <reference types="@javatypes/spring-jdbc" />
export * from './database' export * from './database'
export * from './manager' export * from './manager'

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/i18n", "name": "@ccms/i18n",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript i18n package", "description": "MiaoScript i18n package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,14 +19,14 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@types/js-yaml": "^3.12.3", "@ccms/nashorn": "^0.8.3",
"@types/js-yaml": "^3.12.5",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/nashorn": "^0.7.0", "js-yaml": "^3.14.0"
"js-yaml": "^3.13.1"
}, },
"gitHead": "781524f83e52cad26d7c480513e3c525df867121" "gitHead": "781524f83e52cad26d7c480513e3c525df867121"
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/nashorn", "name": "@ccms/nashorn",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript api package", "description": "MiaoScript api package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -22,6 +22,6 @@
"devDependencies": { "devDependencies": {
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
} }
} }

View File

@@ -13,8 +13,8 @@ declare global {
namespace Java { namespace Java {
function type<T = any>(clazz: string): T function type<T = any>(clazz: string): T
function from<T>(javaObj: T[]): T[] function from<T = any>(javaObj: T[]): T[]
function to<T>(array: T[], type?: T): T[] function to<T = any>(array: T[], type?: T): T[]
function extend(...parentTypes: any[]): any function extend(...parentTypes: any[]): any
function synchronized(func: () => void, lock: any): Function function synchronized(func: () => void, lock: any): Function
function asJSONCompatible<T = any>(obj: T): T function asJSONCompatible<T = any>(obj: T): T

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/nodejs", "name": "@ccms/nodejs",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript nodejs package", "description": "MiaoScript nodejs package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,12 +19,10 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.8.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
},
"dependencies": {
"@ccms/nashorn": "^0.7.0"
}, },
"gitHead": "781524f83e52cad26d7c480513e3c525df867121" "gitHead": "781524f83e52cad26d7c480513e3c525df867121"
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/nukkit", "name": "@ccms/nukkit",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript nukkit package", "description": "MiaoScript nukkit package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,13 +19,14 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@javatypes/nukkit-api": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/common": "^0.7.0", "@ccms/common": "^0.8.3",
"@ccms/container": "^0.7.0" "@ccms/container": "^0.8.3"
} }
} }

View File

@@ -1,4 +1,4 @@
/// <reference types="@ccms/types/dist/typings/nukkit" /> /// <reference types="@javatypes/nukkit-api" />
import { server } from '@ccms/api' import { server } from '@ccms/api'
import { Container } from '@ccms/container' import { Container } from '@ccms/container'

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/ployfill", "name": "@ccms/ployfill",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript ployfill package", "description": "MiaoScript ployfill package",
"author": "MiaoWoo <admin@yumc.pw>", "author": "MiaoWoo <admin@yumc.pw>",
"homepage": "https://github.com/circlecloud/ms.git", "homepage": "https://github.com/circlecloud/ms.git",
@@ -14,13 +14,14 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"@ccms/i18n": "^0.7.0", "@ccms/i18n": "^0.8.3",
"@ccms/nashorn": "^0.7.0", "@ccms/nodejs": "^0.8.3",
"core-js": "^3.6.5" "core-js": "^3.6.5"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.8.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
} }
} }

View File

@@ -1,5 +1,5 @@
/// <reference types="@ccms/nashorn" /> /// <reference types="@ccms/nashorn" />
import '@ccms/nodejs'
import i18n from '@ccms/i18n' import i18n from '@ccms/i18n'
let ployfillStartTime = new Date().getTime() let ployfillStartTime = new Date().getTime()
i18n.initialize() i18n.initialize()

View File

@@ -28,8 +28,9 @@ class Process extends EventEmitter {
queueMicrotask(func: Function) { queueMicrotask(func: Function) {
microTaskPool.execute(func) microTaskPool.execute(func)
} }
exit() { exit(code: number) {
microTaskPool.shutdown(); process.emit('exit', code)
microTaskPool.shutdown()
} }
} }
global.setGlobal('process', new Process(), {}) global.setGlobal('process', new Process(), {})

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/plugin", "name": "@ccms/plugin",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript api package", "description": "MiaoScript api package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,16 +19,16 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@types/js-yaml": "^3.12.3", "@types/js-yaml": "^3.12.5",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/common": "^0.7.0", "@ccms/common": "^0.8.3",
"@ccms/container": "^0.7.0", "@ccms/container": "^0.8.3",
"@ccms/i18n": "^0.7.0", "@ccms/i18n": "^0.8.3",
"js-yaml": "^3.13.1" "js-yaml": "^3.14.0"
} }
} }

View File

@@ -79,7 +79,8 @@ export function config(metadata: interfaces.ConfigMetadata = {}) {
} }
} }
function stage(metadata: interfaces.ExecMetadata = {}, stage: string) { function stage(stage: string) {
return (metadata: interfaces.ExecMetadata = {}) => {
return function (target: any, key: string, value: any) { return function (target: any, key: string, value: any) {
metadata.name = metadata.name || key metadata.name = metadata.name || key
metadata.executor = key metadata.executor = key
@@ -87,15 +88,7 @@ function stage(metadata: interfaces.ExecMetadata = {}, stage: string) {
Reflect.defineMetadata(METADATA_KEY.stage[stage], [metadata, ...previousMetadata], target.constructor) Reflect.defineMetadata(METADATA_KEY.stage[stage], [metadata, ...previousMetadata], target.constructor)
} }
} }
export function load(metadata: interfaces.ExecMetadata = {}) {
return stage(metadata, 'load')
}
export function enable(metadata: interfaces.ExecMetadata = {}) {
return stage(metadata, 'enable')
}
export function disable(metadata: interfaces.ExecMetadata = {}) {
return stage(metadata, 'disable')
} }
export const load = stage('load')
export const enable = stage('enable')
export const disable = stage('disable')

View File

@@ -10,9 +10,14 @@ export class BasicLoader implements plugin.PluginLoader {
constructor() { constructor() {
this.pluginRequireMap = new Map() this.pluginRequireMap = new Map()
} }
require(target: any, result: any) { require(loadMetadata: plugin.PluginLoadMetadata) {
this.pluginRequireMap.set(target.toString(), result) let metadata = loadMetadata.instance?.description
return result if (metadata?.type == this.type) {
loadMetadata.metadata = metadata
loadMetadata.loaded = true
this.pluginRequireMap.set(metadata.source.toString(), loadMetadata.instance)
}
return loadMetadata
} }
build(metadata: plugin.PluginMetadata) { build(metadata: plugin.PluginMetadata) {
return this.pluginRequireMap.get(metadata.source.toString()) return this.pluginRequireMap.get(metadata.source.toString())

View File

@@ -18,8 +18,13 @@ export class IocLoader implements plugin.PluginLoader {
this.pluginMetadataMap = getPluginSources() this.pluginMetadataMap = getPluginSources()
} }
require(target: any, result: any) { require(loadMetadata: plugin.PluginLoadMetadata) {
return this.pluginMetadataMap.get(target.toString()) let metadata = this.pluginMetadataMap.get(loadMetadata.file.toString())
if (metadata && metadata.type == this.type) {
loadMetadata.metadata = metadata
loadMetadata.loaded = true
}
return loadMetadata
} }
build(metadata: plugin.PluginMetadata) { build(metadata: plugin.PluginMetadata) {

View File

@@ -56,6 +56,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
this.loaderMap.set(loader.type, loader) this.loaderMap.set(loader.type, loader)
}) })
this.initialized = true this.initialized = true
process.emit('plugin.initialize')
} }
} }
@@ -64,18 +65,25 @@ export class PluginManagerImpl implements plugin.PluginManager {
this.initialize() this.initialize()
for (const [, scanner] of this.sacnnerMap) { for (const [, scanner] of this.sacnnerMap) {
try { try {
scanner.scan(folder).forEach(file => { scanner.scan(folder).forEach(loadMetadata => {
this.loadPlugin(file, scanner) try {
this.loadPlugin(scanner.load(loadMetadata))
} catch (error) {
console.error(`plugin scanner ${scanner.type} load ${loadMetadata.name} occurred error ${error}`)
console.ex(error)
}
}) })
} catch (error) { } catch (error) {
console.error(`plugin scanner ${scanner.type} occurred error ${error}`) console.error(`plugin scanner ${scanner.type} occurred error ${error}`)
console.ex(error) console.ex(error)
} }
} }
process.emit('plugin.scan', folder)
} }
build(): void { build(): void {
this.buildPlugins() this.buildPlugins()
process.emit('plugin.build')
} }
private logStage(plugin: plugin.Plugin, stage: string) { private logStage(plugin: plugin.Plugin, stage: string) {
@@ -89,28 +97,39 @@ export class PluginManagerImpl implements plugin.PluginManager {
ext() ext()
this.runCatch(plugin, stage) this.runCatch(plugin, stage)
this.runCatch(plugin, `${this.serverType}${stage}`) this.runCatch(plugin, `${this.serverType}${stage}`)
plugin.description.loader[stage](plugin) plugin.description.loadMetadata.loader[stage](plugin)
} catch (ex) { } catch (ex) {
console.i18n("ms.plugin.manager.stage.exec.error", { plugin: plugin.description.name, executor: stage, error: ex }) console.i18n("ms.plugin.manager.stage.exec.error", { plugin: plugin.description.name, executor: stage, error: ex })
} }
} }
private loadPlugin(file: string, scanner: plugin.PluginScanner) { private loadPlugin(loadMetadata: plugin.PluginLoadMetadata) {
if (!loadMetadata) { throw new Error('loadMetadata can\'t be undefiend when loadPlugin!') }
if (loadMetadata.loaded) { throw new Error(`Plugin ${loadMetadata.name} is already loaded by ${loadMetadata.loader?.type}!`) }
try { try {
let requireInstance = scanner.load(file)
for (const [, loader] of this.loaderMap) { for (const [, loader] of this.loaderMap) {
let metadata = loader.require(file, requireInstance) try {
if (metadata && metadata.source && metadata.name) { if (loader.require(loadMetadata).loaded) {
metadata.loader = loader loadMetadata.loader = loader
let metadata = loadMetadata.metadata
this.metadataMap.set(metadata.name, metadata) this.metadataMap.set(metadata.name, metadata)
metadata.loadMetadata = loadMetadata
return metadata return metadata
} }
} catch (error) {
if (global.debug) {
console.console(`§6Loader §b${loader.type} §6load §a${loadMetadata.file} §cerror. §4Err: §c${error}`)
console.ex(error)
} else {
console.warn(`Loader ${loader.type} load ${loadMetadata.file} error. Err: ${error}`)
}
}
} }
} catch (error) { } catch (error) {
console.i18n("ms.plugin.manager.initialize.error", { name: file, ex: error }) console.i18n("ms.plugin.manager.initialize.error", { name: loadMetadata.file, ex: error })
console.ex(error) console.ex(error)
} }
console.console(`§efile §b${file} §ccan't load metadata. §eskip load!`) console.console(`§6scanner: §b${loadMetadata.scanner.type} §ccan\'t load §6file §b${loadMetadata.file}. §eskip!`)
} }
/** /**
@@ -118,10 +137,10 @@ export class PluginManagerImpl implements plugin.PluginManager {
* @param file java.io.File * @param file java.io.File
*/ */
loadFromFile(file: string, scanner = this.sacnnerMap.get('file')): plugin.Plugin { loadFromFile(file: string, scanner = this.sacnnerMap.get('file')): plugin.Plugin {
if (!file) { throw new Error('plugin file can\'t be null!') } if (!file) { throw new Error('plugin file can\'t be undefiend!') }
if (!scanner) { throw new Error('plugin scanner can\'t be null!') } if (!scanner) { throw new Error('plugin scanner can\'t be undefiend!') }
let metadata = this.loadPlugin(file, scanner) let metadata = this.loadPlugin(scanner.read(file))
let plugin = metadata.loader.build(metadata) let plugin = metadata.loadMetadata.loader.build(metadata)
this.load(plugin) this.load(plugin)
this.enable(plugin) this.enable(plugin)
return plugin return plugin
@@ -157,7 +176,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
reload(...args: any[]): void { reload(...args: any[]): void {
this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => { this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => {
this.disable(pl) this.disable(pl)
this.loadFromFile(pl.description.source, pl.description.scanner) this.loadFromFile(pl.description.source.toString(), pl.description.loadMetadata.scanner)
}) })
} }
@@ -179,10 +198,11 @@ export class PluginManagerImpl implements plugin.PluginManager {
} }
private checkAndGet(name: string | plugin.Plugin | undefined | any): Map<string, plugin.Plugin> | plugin.Plugin[] { private checkAndGet(name: string | plugin.Plugin | undefined | any): Map<string, plugin.Plugin> | plugin.Plugin[] {
if (name === undefined) throw new Error(`checkAndGet Plugin can't be undefiend!`)
if (name == this.instanceMap) { return this.instanceMap } if (name == this.instanceMap) { return this.instanceMap }
if (typeof name == 'string' && this.instanceMap.has(name)) { return [this.instanceMap.get(name)] } if (typeof name == 'string' && this.instanceMap.has(name)) { return [this.instanceMap.get(name)] }
if (name instanceof interfaces.Plugin) { return [name as plugin.Plugin] } if (name instanceof interfaces.Plugin) { return [name as plugin.Plugin] }
if (name.description || name.description.name) { return [name as plugin.Plugin] } if (name.description?.name) { return [name as plugin.Plugin] }
throw new Error(`Plugin ${JSON.stringify(name)} not exist!`) throw new Error(`Plugin ${JSON.stringify(name)} not exist!`)
} }
@@ -206,7 +226,9 @@ export class PluginManagerImpl implements plugin.PluginManager {
console.error(`§4无法加载插件 §c${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`) console.error(`§4无法加载插件 §c${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`)
continue continue
} }
(pluginInstance = this.loaderMap.get(metadata.type).build(metadata)) && this.instanceMap.set(metadata.name, pluginInstance) pluginInstance = this.loaderMap.get(metadata.type).build(metadata)
if (!pluginInstance) { console.error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`); continue }
this.instanceMap.set(metadata.name, pluginInstance)
} }
} }

View File

@@ -6,15 +6,20 @@ import { provideSingletonNamed } from "@ccms/container"
export class JSFileScanner implements plugin.PluginScanner { export class JSFileScanner implements plugin.PluginScanner {
type: string = 'file' type: string = 'file'
scan(target: any): string[] { scan(target: any): plugin.PluginLoadMetadata[] {
return this.scanFolder(fs.concat(root, target)) return this.scanFolder(fs.concat(root, target)).map((file) => this.read(file))
} }
load(file: string) { read(file: any): plugin.PluginLoadMetadata {
if (typeof file === "string") { return } return { file, type: this.type, scanner: this, loaded: false }
this.updatePlugin(file) }
load(metadata: plugin.PluginLoadMetadata): plugin.PluginLoadMetadata {
if (metadata.type !== this.type) { return }
this.updatePlugin(metadata.file)
//@ts-ignore //@ts-ignore
return require(file.toString(), { cache: false }) metadata.instance = require(metadata.file.toString(), { cache: false })
return metadata
} }
private scanFolder(folder: any): string[] { private scanFolder(folder: any): string[] {

View File

@@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "@ccms/plugins", "name": "@ccms/plugins",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript plugins package", "description": "MiaoScript plugins package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -20,13 +20,16 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@javatypes/spring-data-redis": "^0.0.2",
"@javatypes/spring-web": "^0.0.2",
"@javatypes/tomcat": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/container": "^0.7.0", "@ccms/container": "^0.8.3",
"@ccms/plugin": "^0.7.0" "@ccms/plugin": "^0.8.3"
} }
} }

View File

@@ -1,7 +1,6 @@
/// <reference types="@ccms/types/dist/typings/bukkit" /> /// <reference types="@javatypes/bungee-api" />
/// <reference types="@ccms/types/dist/typings/sponge" /> /// <reference types="@javatypes/spigot-api" />
/// <reference types="@ccms/types/dist/typings/bungee" /> /// <reference types="@javatypes/sponge-api" />
/// <reference types="@ccms/types/dist/typings/nukkit" />
import { server } from '@ccms/api'; import { server } from '@ccms/api';
import { inject } from '@ccms/container'; import { inject } from '@ccms/container';

View File

@@ -1,6 +1,6 @@
/// <reference types="@ccms/types/dist/typings/bukkit" /> /// <reference types="@javatypes/bungee-api" />
/// <reference types="@ccms/types/dist/typings/sponge" /> /// <reference types="@javatypes/spigot-api" />
/// <reference types="@ccms/types/dist/typings/bungee" /> /// <reference types="@javatypes/sponge-api" />
import { server, plugin as pluginApi, channel, constants } from '@ccms/api' import { server, plugin as pluginApi, channel, constants } from '@ccms/api'
import { inject, optional } from '@ccms/container'; import { inject, optional } from '@ccms/container';

View File

@@ -1,7 +1,4 @@
/// <reference types="@ccms/nashorn" /> /// <reference types="@ccms/nashorn" />
/// <reference types="@ccms/types/dist/typings/bukkit" />
/// <reference types="@ccms/types/dist/typings/sponge" />
/// <reference types="@ccms/types/dist/typings/bungee" />
import { plugin as pluginApi, server, task, constants } from '@ccms/api' import { plugin as pluginApi, server, task, constants } from '@ccms/api'
import { plugin, interfaces, cmd, tab, enable, config, disable } from '@ccms/plugin' import { plugin, interfaces, cmd, tab, enable, config, disable } from '@ccms/plugin'

View File

@@ -1,4 +1,4 @@
/// <reference types="@ccms/types" /> /// <reference types="@javatypes/jdk" />
import { task, server } from "@ccms/api"; import { task, server } from "@ccms/api";
import { inject } from "@ccms/container"; import { inject } from "@ccms/container";

View File

@@ -1,5 +1,3 @@
/// <reference types="@ccms/types" />
import { task, server, constants } from "@ccms/api"; import { task, server, constants } from "@ccms/api";
import { inject } from "@ccms/container"; import { inject } from "@ccms/container";
import { plugin, interfaces, cmd } from "@ccms/plugin"; import { plugin, interfaces, cmd } from "@ccms/plugin";

View File

@@ -1,12 +1,11 @@
/// <reference types="@ccms/types" /> /// <reference types="@javatypes/tomcat" />
/// <reference types="@ccms/types/dist/typings/tomcat/index" /> /// <reference types="@javatypes/spring-web" />
/// <reference types="@ccms/types/dist/typings/spring/index" />
import { constants, database, plugin, web } from "@ccms/api" import { constants, database, plugin, web } from "@ccms/api"
import { inject, ContainerInstance, Container } from "@ccms/container" import { inject, ContainerInstance, Container, JSClass, postConstruct } from "@ccms/container"
import { JSPlugin, interfaces, cmd } from "@ccms/plugin" import { JSPlugin, interfaces, cmd } from "@ccms/plugin"
import { DataBase, DataBaseManager } from '@ccms/database' import { DataBase, DataBaseManager } from '@ccms/database'
import { Server, Context, RequestHandler } from '@ccms/web' import { Server, Context, RequestHandler, Controller, Get, Post, Param, Body } from '@ccms/web'
import * as fs from '@ccms/common/dist/fs' import * as fs from '@ccms/common/dist/fs'
import * as reflect from '@ccms/common/dist/reflect' import * as reflect from '@ccms/common/dist/reflect'
@@ -22,6 +21,9 @@ export class MiaoSpring extends interfaces.Plugin {
@inject(web.Server) @inject(web.Server)
private webServer: Server private webServer: Server
@JSClass('org.springframework.http.HttpStatus')
private HttpStatus: org.springframework.http.HttpStatus
private ResponseEntity = org.springframework.http.ResponseEntity private ResponseEntity = org.springframework.http.ResponseEntity
private mainDatabase: DataBase private mainDatabase: DataBase
@@ -56,14 +58,14 @@ export class MiaoSpring extends interfaces.Plugin {
preHandle: (ctx: Context) => { preHandle: (ctx: Context) => {
const index = foundMap.indexOf(ctx.request.getRequestURI()) const index = foundMap.indexOf(ctx.request.getRequestURI())
if (index != -1) { if (index != -1) {
return this.ResponseEntity.status(org.springframework.http.HttpStatus.FOUND).header('Location', foundMap[index + 1]).build() return this.ResponseEntity.status(this.HttpStatus.FOUND).header('Location', foundMap[index + 1]).build()
} }
} }
}) })
this.webServer.registryInterceptor({ this.webServer.registryInterceptor({
name: 'StaticHandle', name: 'StaticHandle',
preHandle: (ctx: Context) => { preHandle: (ctx: Context) => {
let type = ctx.header['Accept'] || '' let type = ctx.headers['Accept'] || ''
if (type == '*/*' || type.includes('text/css') || type.includes('text/html')) { if (type == '*/*' || type.includes('text/css') || type.includes('text/html')) {
let filePath = fs.concat(root, 'wwwroot', (ctx.request.getRequestURI() == '/' ? 'index.html' : ctx.request.getRequestURI())) let filePath = fs.concat(root, 'wwwroot', (ctx.request.getRequestURI() == '/' ? 'index.html' : ctx.request.getRequestURI()))
let fileType = 'text/html;charset=utf-8' let fileType = 'text/html;charset=utf-8'
@@ -71,33 +73,25 @@ export class MiaoSpring extends interfaces.Plugin {
if (fs.exists(filePath)) { if (fs.exists(filePath)) {
if (filePath.endsWith('.js')) { fileType = 'application/javascript' } if (filePath.endsWith('.js')) { fileType = 'application/javascript' }
if (filePath.endsWith('.css')) { fileType = 'text/css' } if (filePath.endsWith('.css')) { fileType = 'text/css' }
// @ts-ignore
return this.ResponseEntity.ok().header('Content-Type', fileType).body(base.read(filePath)) return this.ResponseEntity.ok().header('Content-Type', fileType).body(base.read(filePath))
} }
} }
} }
}) })
this.webServer.registryMapping('/api/eval', (ctx: Context) => { this.registryMapping('/api/eval', (ctx: Context) => {
try { try {
return { status: 200, data: this.runCode(ctx.body + ''), msg: '代码执行成功!' } return { status: 200, data: this.runCode(ctx.body + ''), msg: '代码执行成功!' }
} catch (error) { } catch (error) {
return { status: 500, data: console.stack(error, false), msg: '代码执行异常!' } return { status: 500, data: console.stack(error, false), msg: '代码执行异常!' }
} }
}) })
this.webServer.registryMapping('/api/plugin/list', () => {
return { status: 200, data: [...this.pluginManager.getPlugins().values()].map((plugin) => plugin.description), msg: '插件列表获取成功!' }
})
this.webServer.registryMapping('/api/plugin/update', (ctx: Context) => {
if (!ctx.params.name) { return { status: 400, msg: '插件名称不得为空!' } }
})
} }
private registryPages() { private registryPages() {
this.webServer.registryMapping('/api/page/list', () => { this.registryMapping('/api/page/list', () => {
return { status: 0, data: { rows: this.mainDatabase.query('SELECT `id`, `type`, `name`, `content` FROM `pages` WHERE `deleted` = 0') } } return { status: 0, data: { rows: this.mainDatabase.query('SELECT `id`, `type`, `name`, `content` FROM `pages` WHERE `deleted` = 0') } }
}) })
this.webServer.registryMapping('/api/page/get', (ctx: Context) => { this.registryMapping('/api/page/get', (ctx: Context) => {
let name = decodeURIComponent(`${ctx.params.name}`) let name = decodeURIComponent(`${ctx.params.name}`)
let varable = undefined let varable = undefined
if (!name) { return { status: 400, msg: '名称不能为空!' } } if (!name) { return { status: 400, msg: '名称不能为空!' } }
@@ -117,20 +111,20 @@ export class MiaoSpring extends interfaces.Plugin {
} }
return { status: 0, data: JSON.parse(content) } return { status: 0, data: JSON.parse(content) }
}) })
this.webServer.registryMapping('/api/page/add', (ctx: Context) => { this.registryMapping('/api/page/add', (ctx: Context) => {
let body = ctx.body let body = ctx.body
if (typeof body.content !== "string") { body.content = JSON.stringify(body.content) } if (typeof body.content !== "string") { body.content = JSON.stringify(body.content) }
this.mainDatabase.update("INSERT INTO `pages`(`type`, `name`, `content`) VALUES (?, ?, ?)", body.type || 1, body.name, body.content) this.mainDatabase.update("INSERT INTO `pages`(`type`, `name`, `content`) VALUES (?, ?, ?)", body.type || 1, body.name, body.content)
return { status: 0, msg: `${body.name} 新增成功!` } return { status: 0, msg: `${body.name} 新增成功!` }
}) })
this.webServer.registryMapping('/api/page/update', (ctx: Context) => { this.registryMapping('/api/page/update', (ctx: Context) => {
if (!ctx.params.id) { return { status: 400, msg: 'ID 不能为空!' } } if (!ctx.params.id) { return { status: 400, msg: 'ID 不能为空!' } }
const body = ctx.body const body = ctx.body
if (typeof body.content !== "string") { body.content = JSON.stringify(body.content) } if (typeof body.content !== "string") { body.content = JSON.stringify(body.content) }
this.mainDatabase.update("UPDATE `pages` SET `name` = ?, `content` = ? WHERE id = ?", body.name, body.content, ctx.params.id) this.mainDatabase.update("UPDATE `pages` SET `name` = ?, `content` = ? WHERE id = ?", body.name, body.content, ctx.params.id)
return { status: 0, msg: `${body.name} 更新成功!` } return { status: 0, msg: `${body.name} 更新成功!` }
}) })
this.webServer.registryMapping('/api/page/delete', (ctx: Context) => { this.registryMapping('/api/page/delete', (ctx: Context) => {
if (!ctx.params.name) { return { status: 400, msg: '页面 名称 不能为空!' } } if (!ctx.params.name) { return { status: 400, msg: '页面 名称 不能为空!' } }
this.mainDatabase.update("UPDATE `pages` SET `name` = CONCAT(name, '_deleted'), deleted = 1 WHERE name = ?", ctx.params.name) this.mainDatabase.update("UPDATE `pages` SET `name` = CONCAT(name, '_deleted'), deleted = 1 WHERE name = ?", ctx.params.name)
return { status: 0, msg: `${ctx.params.name} 删除成功!` } return { status: 0, msg: `${ctx.params.name} 删除成功!` }
@@ -140,17 +134,17 @@ export class MiaoSpring extends interfaces.Plugin {
private configTable = "config" private configTable = "config"
private registryDatabase() { private registryDatabase() {
this.webServer.registryMapping('/api/config/list', (ctx: Context) => { this.registryMapping('/api/config/list', (ctx: Context) => {
return { status: 0, data: this.mainDatabase.query('SELECT id, name, label, url, driver, username, password FROM `' + this.configTable + '` WHERE deleted = 0 AND type = ?', ctx.params.type || 0) } return { status: 0, data: this.mainDatabase.query('SELECT id, name, label, url, driver, username, password FROM `' + this.configTable + '` WHERE deleted = 0 AND type = ?', ctx.params.type || 0) }
}) })
this.webServer.registryMapping('/api/config/get', (ctx: Context) => { this.registryMapping('/api/config/get', (ctx: Context) => {
let name = ctx.params.name let name = ctx.params.name
if (!name) { return { status: 400, msg: '名称不能为空!' } } if (!name) { return { status: 400, msg: '名称不能为空!' } }
let result = this.mainDatabase.query('SELECT id, name, label, url, driver, username, password FROM `' + this.configTable + '` WHERE `name` = ?', name) let result = this.mainDatabase.query('SELECT id, name, label, url, driver, username, password FROM `' + this.configTable + '` WHERE `name` = ?', name)
if (!result.length) { return { status: 404, msg: `配置 ${name} 不存在!` } } if (!result.length) { return { status: 404, msg: `配置 ${name} 不存在!` } }
return { status: 0, data: result[0] } return { status: 0, data: result[0] }
}) })
this.webServer.registryMapping('/api/config/add', (ctx: Context) => { this.registryMapping('/api/config/add', (ctx: Context) => {
let body = ctx.body let body = ctx.body
if (!body.name) { return { status: 400, msg: '名称不能为空!' } } if (!body.name) { return { status: 400, msg: '名称不能为空!' } }
this.mainDatabase.update( this.mainDatabase.update(
@@ -159,7 +153,7 @@ export class MiaoSpring extends interfaces.Plugin {
) )
return { status: 0, msg: `配置 ${body.name} 新增成功!` } return { status: 0, msg: `配置 ${body.name} 新增成功!` }
}) })
this.webServer.registryMapping('/api/config/update', (ctx: Context) => { this.registryMapping('/api/config/update', (ctx: Context) => {
if (!ctx.params.id) { return { status: 400, msg: 'ID 不能为空!' } } if (!ctx.params.id) { return { status: 400, msg: 'ID 不能为空!' } }
let body = ctx.body let body = ctx.body
this.mainDatabase.update( this.mainDatabase.update(
@@ -195,3 +189,44 @@ return eval(${JSON.stringify(code)});`)
Object.keys(this.mappings).forEach((r) => this.webServer.unregistryMapping(r)) Object.keys(this.mappings).forEach((r) => this.webServer.unregistryMapping(r))
} }
} }
class Plugin {
id: number
name: string
source: string
}
@Controller()
class PluginController {
@inject(plugin.PluginManager)
private pluginManager: plugin.PluginManager
@inject(database.DataBaseManager)
private databaseManager: DataBaseManager
private mainDB: DataBase
@postConstruct()
initialize() {
this.mainDB = this.databaseManager.getMainDatabase()
}
@Get()
list(@Param('install') install: boolean) {
if (install) {
return { status: 200, data: [...this.pluginManager.getPlugins().values()].map((plugin) => plugin.description), msg: '插件列表获取成功!' }
} else {
return { status: 200, data: this.mainDB.query<Plugin>("SELECT name FROM plugins WHERE deleted = 0") }
}
}
@Post()
deploy(@Body() info: Plugin) {
let plugin = this.mainDB.query<Plugin>("SELECT name FROM plugins WHERE name = ?", info.name)
if (plugin.length == 0) {
this.mainDB.update("INSERT INTO `plugins`(`name`, `source`) VALUES (?, ?)", info.name, info.source)
return { status: 200, msg: `插件 ${info.name} 新增成功!` }
} else {
this.mainDB.update("UPDATE `plugins` SET `source` = ? WHERE id = ?", info.source, plugin[0].id)
return { status: 200, msg: `插件 ${info.name} 更新成功!` }
}
}
}

View File

@@ -0,0 +1,39 @@
import { constants, web } from "@ccms/api"
import { inject } from "@ccms/container"
import { plugin, interfaces } from "@ccms/plugin"
import { Controller, Get, Server, Body, Post, Cookie, Header, Param } from "@ccms/web"
@plugin({ name: 'MiaoWeb', version: '1.0.0', author: 'MiaoWoo', servers: [constants.ServerType.Spring], source: __filename })
export class MiaoWeb extends interfaces.Plugin {
@inject(web.Server)
private webServer: Server
enable() {
this.webServer.registryController(TestController)
}
disable() {
this.webServer.unregistryController(TestController)
}
}
@Controller()
export class TestController {
@Get()
public get() {
return 'test'
}
@Post()
public post(@Body() body: any,
@Cookie('test') testCookie,
@Header('Accept') testHeader,
@Param("name") param) {
return {
body,
testCookie,
testHeader,
param
}
}
}

View File

@@ -1,11 +1,16 @@
/// <reference types="@ccms/types/dist/typings/rabbitmq" /> /// <reference types="@javatypes/spring-data-redis" />
/// <reference types="@ccms/types/dist/typings/spring/amqp" />
import { constants, plugin as pluginApi, amqp, server, web } from '@ccms/api' import { constants, plugin as pluginApi, amqp, server, web } from '@ccms/api'
import { plugin, interfaces, cmd } from '@ccms/plugin' import { plugin, interfaces, cmd } from '@ccms/plugin'
import { AmqpAdmin, ConnectionFactoryAdapter, AmqpManager } from '@ccms/amqp' import { AmqpAdmin, ConnectionFactoryAdapter, AmqpManager } from '@ccms/amqp'
import { inject, Autowired } from '@ccms/container' import { inject, Autowired, ContainerInstance, Container } from '@ccms/container'
import { Server } from '@ccms/web' import { Server, Controller, Get, Param, METADATA_KEY } from '@ccms/web'
const SearchRankingAmqpAdmin = Symbol('SearchRanking-AmqpAdmin')
const EXCHANGE_NAME = `search.ranking.${process.env.SPRING_CLOUD_CONFIG_PROFILE || 'dev'}`
const QUEUE_NAME = `search.ranking.${process.env.SPRING_CLOUD_CONFIG_PROFILE || 'dev'}`
const ROUTER_KEY = `search.ranking.${process.env.SPRING_CLOUD_CONFIG_PROFILE || 'dev'}`
@plugin({ name: SearchRanking.name, version: SearchRanking.version, author: SearchRanking.author, servers: SearchRanking.servers, source: __filename }) @plugin({ name: SearchRanking.name, version: SearchRanking.version, author: SearchRanking.author, servers: SearchRanking.servers, source: __filename })
export class SearchRanking extends interfaces.Plugin { export class SearchRanking extends interfaces.Plugin {
@@ -13,6 +18,8 @@ export class SearchRanking extends interfaces.Plugin {
public static author = 'MiaoWoo' public static author = 'MiaoWoo'
public static servers = [constants.ServerType.Spring] public static servers = [constants.ServerType.Spring]
@inject(ContainerInstance)
private container: Container
@inject(pluginApi.PluginManager) @inject(pluginApi.PluginManager)
private pluginManager: pluginApi.PluginManager private pluginManager: pluginApi.PluginManager
@inject(amqp.Manager) @inject(amqp.Manager)
@@ -23,17 +30,11 @@ export class SearchRanking extends interfaces.Plugin {
private webServer: Server private webServer: Server
@Autowired() @Autowired()
private mongoTemplate: any private redisTemplate: org.springframework.data.redis.core.RedisTemplate<string, any>
@Autowired()
private redisTemplate: any
private amqpAdmin: AmqpAdmin private amqpAdmin: AmqpAdmin
private listener: org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer private listener: org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer
private readonly exchangeName = 'search.ranking'
private readonly queueName = 'search.ranking'
private readonly routerKey = 'search.ranking'
load() { load() {
let connection = new ConnectionFactoryAdapter({ let connection = new ConnectionFactoryAdapter({
url: 'amqp://rabbitmq.c.sixi.com:5672', url: 'amqp://rabbitmq.c.sixi.com:5672',
@@ -45,33 +46,26 @@ export class SearchRanking extends interfaces.Plugin {
} }
enable() { enable() {
this.webServer.registryMapping('/api/search', (ctx) => { this.amqpAdmin.declareQueueAndBindExchange(QUEUE_NAME, EXCHANGE_NAME, ROUTER_KEY)
if (!ctx.params.keyword) { return { status: 400, msg: '查询关键词不得为空!' } } this.amqpAdmin.declareBinding(QUEUE_NAME, 'client.topic.exchange', `cmd_res.${ROUTER_KEY}`)
let keyword = ctx.params.keyword + '' this.listener = this.amqpAdmin.createContainer<string>(QUEUE_NAME, (content, _message, _channel) => {
let type = ctx.params.type == 'sale' ? 'sale' : 'normal'
let time = parseInt(ctx.params.time + '') || 60 * 60 * 24 * 15
return this.cacheAndSearch(keyword, type as any, time * 1000)
})
this.webServer.registryMapping('/api/search/ranking', (ctx) => {
if (!ctx.params.keyword || !ctx.params.shopName) { return { status: 400, msg: '查询关键词不得为空!' } }
let keyword = ctx.params.keyword + ''
let shopName = ctx.params.shopName + ''
let type = ctx.params.type == 'sale' ? 'sale' : 'normal'
let time = parseInt(ctx.params.time + '') || 60 * 60 * 24 * 15
return this.sendSearchRankingCmd(keyword, shopName, type as any, time * 1000)
})
this.amqpAdmin.declareQueueAndBindExchange(this.queueName, this.exchangeName, this.routerKey)
this.amqpAdmin.declareBinding(this.queueName, 'client.topic.exchange', `cmd_res.${this.routerKey}`)
this.listener = this.amqpAdmin.createContainer<string>(this.queueName, (content, message, channel) => {
let searchResult = JSON.parse(content) let searchResult = JSON.parse(content)
if (searchResult.type == "company") {
this.logger.info(content)
return
}
this.redisTemplate.opsForValue().set(searchResult.reqData.cacheKey, searchResult) this.redisTemplate.opsForValue().set(searchResult.reqData.cacheKey, searchResult)
this.logger.sender(this.Server.getConsoleSender(), `§6查询任务完成! §b关键词: §r${searchResult.reqData.keywords}`) this.logger.sender(this.Server.getConsoleSender(), `§6查询任务完成! §b关键词: §r${searchResult.reqData.keywords}`)
}) })
this.container.bind(SearchRankingAmqpAdmin).toConstantValue(this.amqpAdmin)
this.listener.start() this.listener.start()
this.webServer.registryController(SearchRankingController)
} }
disable() { disable() {
this.container.unbind(SearchRankingAmqpAdmin)
this.listener.stop() this.listener.stop()
this.webServer.unregistryController(SearchRankingController)
} }
@cmd() @cmd()
@@ -93,14 +87,55 @@ export class SearchRanking extends interfaces.Plugin {
this.pluginManager.reload(this) this.pluginManager.reload(this)
} }
cmdsend(sender: any, ...args: string[]) {
}
cmdsearch(sender: any, keyword: string, type: "sale" | "normal" = "sale", time: number) { cmdsearch(sender: any, keyword: string, type: "sale" | "normal" = "sale", time: number) {
this.logger.sender(sender, this.cacheAndSearch(keyword, type, time)?.msg) this.logger.sender(sender, this.container.getNamed<SearchRankingController>(METADATA_KEY.Controller, SearchRankingController.name).cacheAndSearch(keyword, type, time)?.msg)
}
} }
private cacheAndSearch(keyword: string, type: string = "sale", time: number = 15 * 24 * 60 * 60 * 1000) { @Controller({ path: '/api', name: SearchRankingController.name })
class SearchRankingController {
@Autowired()
private redisTemplate: org.springframework.data.redis.core.RedisTemplate<string, any>
@inject(SearchRankingAmqpAdmin)
private amqpAdmin: AmqpAdmin
@Get()
public search(
@Param({ name: 'keyword', require: true, message: '查询关键词不得为空!' }) keyword: string,
@Param("type") type: string = 'normal',
@Param('time') time: number = 60 * 60 * 24 * 15
) {
return this.cacheAndSearch(keyword, type as any, time * 1000)
}
@Get("/search/ranking")
public searchRanking(
@Param({ name: 'keyword', require: true, message: '查询关键词不得为空!' }) keyword: string,
@Param("shopName") shopName: string = 'normal',
@Param("type") type: string = 'normal',
@Param('time') time: number = 60 * 60 * 24 * 15
) {
return this.sendSearchRankingCmd(keyword, shopName, type as any, time * 1000)
}
@Get('/search/company')
public searchCompany(
@Param('url') url: string
) {
this.amqpAdmin.getTemplate().convertAndSend('client.topic.exchange', 'cmd_req', {
cmd: 'scout._1688CompanyInfo',
data: {
type: 'company',
url: url,
cacheKey: 'SearchRanking:company'
},
resRouteSuffix: ROUTER_KEY,
target: {}
})
}
public cacheAndSearch(keyword: string, type: string = "sale", time: number = 15 * 24 * 60 * 60 * 1000) {
let cacheKey = this.getCacheKey(keyword, type) let cacheKey = this.getCacheKey(keyword, type)
if (this.redisTemplate.hasKey(cacheKey)) { if (this.redisTemplate.hasKey(cacheKey)) {
let lastSearchTime = this.redisTemplate.opsForValue().get(cacheKey) let lastSearchTime = this.redisTemplate.opsForValue().get(cacheKey)
@@ -146,7 +181,7 @@ export class SearchRanking extends interfaces.Plugin {
} : {}, } : {},
cacheKey: this.getResultCacheKey(keywords, type, dateCache) cacheKey: this.getResultCacheKey(keywords, type, dateCache)
}, },
resRouteSuffix: this.routerKey, resRouteSuffix: ROUTER_KEY,
target: {} target: {}
}) })
} }
@@ -162,11 +197,10 @@ export class SearchRanking extends interfaces.Plugin {
cacheKey: this.getResultCacheKey(keywords, type, cacheTime), cacheKey: this.getResultCacheKey(keywords, type, cacheTime),
cacheTime cacheTime
}, },
resRouteSuffix: this.routerKey, resRouteSuffix: ROUTER_KEY,
target: {} target: {}
}) })
} }
/** /**
* 获得关键词+类型的上次查询时间 * 获得关键词+类型的上次查询时间
* @param keywords 关键词 * @param keywords 关键词

View File

@@ -5,7 +5,9 @@
"outDir": "dist", "outDir": "dist",
"skipLibCheck": true, "skipLibCheck": true,
"sourceMap": false, "sourceMap": false,
"inlineSourceMap": true "inlineSourceMap": true,
"declaration": false,
"declarationMap": false
}, },
"exclude": [ "exclude": [
"dist", "dist",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/sponge", "name": "@ccms/sponge",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript api package", "description": "MiaoScript api package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,13 +19,14 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@javatypes/sponge-api": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/common": "^0.7.0", "@ccms/common": "^0.8.3",
"@ccms/container": "^0.7.0" "@ccms/container": "^0.8.3"
} }
} }

View File

@@ -1,4 +1,4 @@
/// <reference types="@ccms/types/dist/typings/sponge" /> /// <reference types="@javatypes/sponge-api" />
import { server } from '@ccms/api' import { server } from '@ccms/api'
import { Container } from '@ccms/container' import { Container } from '@ccms/container'

View File

@@ -1,18 +1,18 @@
import { server, constants } from '@ccms/api' import { server, constants } from '@ccms/api'
import { provideSingleton } from '@ccms/container'; import { provideSingleton } from '@ccms/container'
import * as reflect from '@ccms/common/dist/reflect' import * as reflect from '@ccms/common/dist/reflect'
const Sponge = org.spongepowered.api.Sponge; const Sponge = org.spongepowered.api.Sponge
const TextSerializers = org.spongepowered.api.text.serializer.TextSerializers; const TextSerializers = org.spongepowered.api.text.serializer.TextSerializers
const File = Java.type("java.io.File"); const File = Java.type("java.io.File")
@provideSingleton(server.Server) @provideSingleton(server.Server)
export class SpongeServer extends server.ReflectServer { export class SpongeServer extends server.ReflectServer {
private pluginsFolder: string; private pluginsFolder: string
constructor() { constructor() {
super(); super()
this.pluginsFolder = new File(base.getInstance().getClass().getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile().getCanonicalPath() this.pluginsFolder = new File(base.getInstance().getClass().getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile().getCanonicalPath()
} }
@@ -35,25 +35,25 @@ export class SpongeServer extends server.ReflectServer {
if (typeof sender === 'string') { if (typeof sender === 'string') {
sender = this.getPlayer(sender) sender = this.getPlayer(sender)
} }
return Sponge.getCommandManager().process(sender, command).getQueryResult().get() return Sponge.getCommandManager().process(sender, command).getQueryResult().get() != 0
} }
dispatchConsoleCommand(command: string): boolean { dispatchConsoleCommand(command: string): boolean {
return Sponge.getCommandManager().process(Sponge.getServer().getConsole(), command).getQueryResult().get() return Sponge.getCommandManager().process(Sponge.getServer().getConsole(), command).getQueryResult().get() != 0
} }
getPluginsFolder(): string { getPluginsFolder(): string {
return this.pluginsFolder; return this.pluginsFolder
} }
getNativePluginManager() { getNativePluginManager() {
return Sponge.getPluginManager() as any; return Sponge.getPluginManager() as any
} }
getDedicatedServer() { getDedicatedServer() {
return reflect.on(Sponge.getServer()).get() return reflect.on(Sponge.getServer()).get()
} }
getNettyPipeline() { getNettyPipeline() {
return this.pipeline; return this.pipeline
} }
getRootLogger() { getRootLogger() {
return this.rootLogger; return this.rootLogger
} }
sendJson(sender: string | any, json: string): void { sendJson(sender: string | any, json: string): void {
if (typeof sender === "string") { if (typeof sender === "string") {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/spring", "name": "@ccms/spring",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript spring package", "description": "MiaoScript spring package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -21,12 +21,12 @@
"devDependencies": { "devDependencies": {
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.7.0", "@ccms/api": "^0.8.3",
"@ccms/common": "^0.7.0", "@ccms/common": "^0.8.3",
"@ccms/container": "^0.7.0", "@ccms/container": "^0.8.3",
"@ccms/database": "^0.7.0" "@ccms/database": "^0.8.3"
} }
} }

View File

@@ -47,7 +47,7 @@ export class CommandMap {
} }
tabComplate(sender: any, input: string, index?: number): string[] { tabComplate(sender: any, input: string, index?: number): string[] {
if (index == 0) { return Object.keys(this.commands) } if (index == 0) { return Object.keys(this.commands).concat('help') }
let [command, ...args] = input.split(' ') let [command, ...args] = input.split(' ')
let exists = this.commands[command] let exists = this.commands[command]
if (exists && exists.tabCompleter) { if (exists && exists.tabCompleter) {

View File

@@ -4,29 +4,45 @@ import { provideSingleton, inject } from '@ccms/container'
import * as fs from '@ccms/common/dist/fs' import * as fs from '@ccms/common/dist/fs'
interface MySQLPlugin {
name: string
source: string
}
@provideSingleton(plugin.PluginScanner) @provideSingleton(plugin.PluginScanner)
export class MySQLScanner implements plugin.PluginScanner { export class MySQLScanner implements plugin.PluginScanner {
type: string = "mysql" type: string = "mysql"
private cacheDir = 'mysql-plugin-cache'
private cacheFileMap: Map<string, MySQLPlugin> = new Map()
private target: string private target: string
@inject(database.DataBaseManager) @inject(database.DataBaseManager)
private databaseManager: DataBaseManager private databaseManager: DataBaseManager
scan(target: any): string[] { scan(target: any): plugin.PluginLoadMetadata[] {
this.target = target this.target = target
let plugins = this.databaseManager.getMainDatabase().query<{ name: string }>(`SELECT name FROM ${this.target} WHERE LENGTH(source) != 0 AND deleted = 0`) let plugins = this.databaseManager.getMainDatabase().query<MySQLPlugin>(`SELECT name FROM ${this.target} WHERE LENGTH(source) != 0 AND deleted = 0`)
return plugins.map(p => `mysql:${p.name}`) return plugins.map(p => this.read(p))
} }
load(target: any) { read(mysqlPlugin: MySQLPlugin): plugin.PluginLoadMetadata {
if (typeof target !== "string" || !target.startsWith('mysql:')) { return } // if invoke this function from loadFromFile mysqlPlugin is a string need read from cache
let name = target.split("mysql:")[1] if (typeof mysqlPlugin == "string") {
if (!name) { console.warn(`[PluginScanner][mysql] plugin name can't be null!`); return } if (!this.cacheFileMap.has(mysqlPlugin)) { throw new Error(`this file ${mysqlPlugin} is not read from mysql-scanner. can't reload from this scanner!`) }
var plugin: any = this.databaseManager.getMainDatabase().query<{ source: string }>(`SELECT source FROM ${this.target} WHERE name = ? AND deleted = 0`, name) mysqlPlugin = this.cacheFileMap.get(mysqlPlugin)
if (plugin.length == 0) { console.warn(`[PluginScanner][mysql] plugin ${target} not found at mysql database...`); return } }
let temp = fs.concat(root, 'mysql-plugin-cache', target, `${plugin[0]}.js`) let cacheFile = fs.concat(root, this.cacheDir, `${mysqlPlugin.name}.js`)
base.save(temp, plugin[0].source) this.cacheFileMap.set(cacheFile, mysqlPlugin)
return { name: mysqlPlugin.name, file: cacheFile, type: this.type, mysqlPlugin, scanner: this }
}
load(metadata: plugin.PluginLoadMetadata) {
if (metadata.type !== this.type) { return }
var plugin: any = this.databaseManager.getMainDatabase().query<MySQLPlugin>(`SELECT source FROM ${this.target} WHERE name = ? AND deleted = 0`, metadata.name)
if (plugin.length == 0) { throw new Error(`[MySQLScanner] plugin ${metadata.name} not found at mysql database...`) }; plugin = plugin[0]
if (!plugin.source) { throw new Error(`[MySQLScanner] plugin ${metadata.name} source can\'t be undefiend or empty...`) }
base.save(metadata.file, plugin.source)
//@ts-ignore //@ts-ignore
return require(temp, { cache: false }) metadata.instance = require(metadata.file, { cache: false })
return metadata
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/web", "name": "@ccms/web",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript web package", "description": "MiaoScript web package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,11 +19,17 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@javatypes/jdk": "^0.0.2",
"@javatypes/spring-beans": "^0.0.2",
"@javatypes/spring-core": "^0.0.2",
"@javatypes/spring-web": "^0.0.2",
"@javatypes/tomcat": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
}, },
"dependencies": { "dependencies": {
"@ccms/container": "^0.7.0" "@ccms/api": "^0.8.3",
"@ccms/container": "^0.8.3"
} }
} }

View File

@@ -1,2 +1,16 @@
export const WebProxyBeanName = 'webServerProxy' export const WebProxyBeanName = 'webServerProxy'
export const FilterProxyBeanName = 'webFilterProxy' export const FilterProxyBeanName = 'webFilterProxy'
export const METADATA_KEY = {
Controller: Symbol("@ccms/web:Controller"),
Action: Symbol("@ccms/web:Action"),
Param: Symbol("@ccms/web:Param"),
Middleware: Symbol("@ccms/web:Middleware"),
}
export enum PARAM_TYPE {
QUERY = 'QUERY',
BODY = 'BODY',
HEADER = 'HEADER',
COOKIE = 'COOKIE',
REQUEST = 'REQUEST',
RESPONSE = 'RESPONSE',
}

View File

@@ -1,30 +1,80 @@
export const Controller = () => { import { decorate, injectable } from "@ccms/container"
return <TFunction extends Function>(target: TFunction): ClassDecorator => {
import { METADATA_KEY, PARAM_TYPE } from '../constants'
import { interfaces } from "../interfaces"
import { addControllerMetadata, addControllerAction, addActionParam } from "./utils"
export const Controller = (metadata?: string | interfaces.ControllerMetadata) => {
return (target: any) => {
if (!metadata) { metadata = target.name.toLowerCase().replace('controller', '') }
if (typeof metadata === "string") { metadata = { path: metadata } }
metadata.name = metadata.name || target.name
metadata.path = metadata.path ?? `/${metadata}`
metadata.path = metadata.path.startsWith('/') ? metadata.path : `/${metadata.path}`
decorate(injectable(), target)
Reflect.defineMetadata(METADATA_KEY.Controller, metadata, target)
addControllerMetadata(metadata)
return return
} }
} }
export const Post = () => {
return <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): MethodDecorator => { function action(method: interfaces.Method) {
return (metadata?: string | interfaces.ActionMetadata) => {
return (target: any, propertyKey: string) => {
if (!metadata) { metadata = propertyKey.toLowerCase() }
if (typeof metadata === "string") { metadata = { path: metadata } }
metadata.path = metadata.path ?? `/${propertyKey}`
metadata.path = metadata.path.startsWith('/') ? metadata.path : `/${metadata.path}`
metadata.method = method
metadata.executor = propertyKey
Reflect.defineMetadata(METADATA_KEY.Action, metadata, target[propertyKey])
addControllerAction(target, propertyKey)
return return
} }
} }
export const Get = () => { }
return <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): MethodDecorator => { export const Action = action('ALL')
export const Get = action('GET')
export const Post = action('POST')
export const Put = action('PUT')
export const Patch = action('PATCH')
export const Head = action('HEAD')
export const Delete = action('DELETE')
function param(type: PARAM_TYPE) {
return (metadata?: string | interfaces.ParamMetadata) => {
return (target: any, propertyKey: string, index: number) => {
if (!metadata) { metadata = `${propertyKey}-${index}` }
if (typeof metadata === "string") { metadata = { name: metadata } }
metadata.type = type
metadata.index = index
metadata.paramtype = Reflect.getMetadata("design:paramtypes", target, propertyKey)[index]
addActionParam(target, propertyKey, metadata)
return return
} }
} }
export const Header = () => { }
return <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): MethodDecorator => { export const Request = param(PARAM_TYPE.REQUEST)
export const Response = param(PARAM_TYPE.RESPONSE)
export const Header = param(PARAM_TYPE.HEADER)
export const Cookie = param(PARAM_TYPE.COOKIE)
export const Query = param(PARAM_TYPE.QUERY)
export const Param = param(PARAM_TYPE.QUERY)
export const Body = param(PARAM_TYPE.BODY)
function Middleware() {
return (metadata?: string | interfaces.ActionMetadata) => {
return (target: any, propertyKey: string) => {
if (!metadata) { metadata = propertyKey.toLowerCase() }
if (typeof metadata === "string") { metadata = { path: metadata } }
metadata.path = metadata.path ?? `/${propertyKey}`
metadata.path = metadata.path.startsWith('/') ? metadata.path : `/${metadata.path}`
metadata.executor = propertyKey
Reflect.defineMetadata(METADATA_KEY.Action, metadata, target[propertyKey])
addControllerAction(target, propertyKey)
return return
} }
} }
export const Param = () => {
return (target: Object, propertyKey: string | symbol, parameterIndex: number): ParameterDecorator => {
return
}
}
export const RequestBody = () => {
return (target: Object, propertyKey: string | symbol, parameterIndex: number): ParameterDecorator => {
return
}
} }
export * from './utils'

View File

@@ -0,0 +1,27 @@
import { interfaces } from '../interfaces'
import { METADATA_KEY } from '../constants'
export function getControllerMetadatas(): interfaces.ControllerMetadata[] {
return Reflect.getMetadata(METADATA_KEY.Controller, Reflect) || []
}
export function addControllerMetadata(metadata: interfaces.ControllerMetadata) {
Reflect.defineMetadata(METADATA_KEY.Controller, [metadata, ...getControllerMetadatas()], Reflect)
}
export function getControllerActions(target: any): string[] {
return Reflect.getMetadata(METADATA_KEY.Action, target.constructor) || []
}
export function addControllerAction(target: any, propertyKey: string) {
Reflect.defineMetadata(METADATA_KEY.Action, [propertyKey, ...getControllerActions(target)], target.constructor)
}
export function getControllerMetadata(target: any): interfaces.ControllerMetadata {
return Reflect.getMetadata(METADATA_KEY.Controller, target)
}
export function getActionMetadata(target: any, propertyKey: string): interfaces.ActionMetadata {
return Reflect.getMetadata(METADATA_KEY.Action, target[propertyKey])
}
export function getActionParams(target: any, propertyKey: string): interfaces.ParamMetadata[] {
return Reflect.getMetadata(METADATA_KEY.Param, target[propertyKey]) || []
}
export function addActionParam(target: any, propertyKey: string, metadata: interfaces.ParamMetadata) {
Reflect.defineMetadata(METADATA_KEY.Param, [metadata, ...getActionParams(target, propertyKey)], target[propertyKey])
}

View File

@@ -1,8 +1,11 @@
/// <reference types="@ccms/types/dist/typings/jdk/index" /> /// <reference types="@ccms/nashorn" />
/// <reference types="@ccms/types/dist/typings/tomcat/index" /> /// <reference types="@javatypes/jdk" />
/// <reference types="@ccms/types/dist/typings/spring/index" /> /// <reference types="@javatypes/tomcat" />
/// <reference types="@ccms/types/dist/typings/spring/beans/index" /> /// <reference types="@javatypes/spring-web" />
/// <reference types="@javatypes/spring-core" />
/// <reference types="@javatypes/spring-beans" />
export * from './server' export * from './server'
export * from './constants'
export * from './decorators' export * from './decorators'
export * from './interfaces' export * from './interfaces'

View File

@@ -5,14 +5,20 @@ export interface InterceptorAdapter {
postHandle?(ctx: Context): void postHandle?(ctx: Context): void
} }
export type RequestHeader = { [key: string]: string | string[] } type StringKeyAndStringValue = { [key: string]: string }
export type RequestParams = { [key: string]: string | string[] } type StringKeyAndStringOrArrayValue = { [key: string]: string | string[] }
export type RequestHeaders = StringKeyAndStringOrArrayValue
export type RequestParams = StringKeyAndStringOrArrayValue
export type RequestCookies = StringKeyAndStringValue
export interface Context { export interface Context {
request?: javax.servlet.http.HttpServletRequest request?: javax.servlet.http.HttpServletRequest
response?: javax.servlet.http.HttpServletResponse response?: javax.servlet.http.HttpServletResponse
header?: RequestHeader handler?: RequestHandler
url?: string url?: string
headers?: RequestHeaders
cookies?: RequestCookies
params?: RequestParams params?: RequestParams
body?: any body?: any
result?: any result?: any

View File

@@ -1,3 +1,6 @@
import { PARAM_TYPE } from "../constants"
export namespace interfaces {
export interface BaseMetadata { export interface BaseMetadata {
/** /**
* 名称 为空则为对象名称 * 名称 为空则为对象名称
@@ -8,7 +11,67 @@ export interface BaseMetadata {
*/ */
servers?: string[] servers?: string[]
} }
export interface WebMetadata extends BaseMetadata {
export interface ControllerMetadata extends BaseMetadata { /**
* 路径
*/
path: string
/**
* 对象
*/
target?: string
}
export interface ControllerMetadata extends WebMetadata {
} }
interface Newable<T> {
new(...args: any[]): T
}
interface Abstract<T> {
prototype: T
}
export type ServiceIdentifier<T> = (string | symbol | Newable<T> | Abstract<T>)
export interface MiddlewareMetadata extends BaseMetadata {
/**
* 中间件名称列表
*/
names: Array<ServiceIdentifier<any>>
}
export type Method = 'ALL' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'TRACE'
export interface ActionMetadata extends WebMetadata {
/**
* 请求方法
*/
method?: Method
/**
* 执行器
*/
executor?: string
}
export interface ParamMetadata extends BaseMetadata {
/**
* 参数类型
*/
type?: PARAM_TYPE
/**
* 默认值
*/
default?: any
/**
* 参数位置
*/
index?: number
/**
* 参数对象类型
*/
paramtype?: NewableFunction
/**
* 是否必传
*/
require?: boolean
/**
* 异常消息
*/
message?: string
}
}

View File

@@ -1,10 +1,16 @@
import * as querystring from 'querystring' import * as querystring from 'querystring'
import { web } from '@ccms/api' import { web } from '@ccms/api'
import { provideSingleton, JSClass, postConstruct } from '@ccms/container' import { provideSingleton, JSClass, postConstruct, Container, ContainerInstance, inject } from '@ccms/container'
import { WebProxyBeanName, FilterProxyBeanName } from './constants' import { WebProxyBeanName, FilterProxyBeanName, METADATA_KEY, PARAM_TYPE } from './constants'
import { Context, InterceptorAdapter, RequestHandler } from './interfaces' import { Context, InterceptorAdapter, RequestHandler, interfaces } from './interfaces'
import { getControllerActions, getActionMetadata, getControllerMetadata, getActionParams } from './decorators'
const HttpServletRequestWrapper = Java.type('javax.servlet.http.HttpServletRequestWrapper')
const HttpServletResponseWrapper = Java.type('javax.servlet.http.HttpServletResponseWrapper')
const ServletInputStream = Java.type('javax.servlet.ServletInputStream')
const ServletOutputStream = Java.type('javax.servlet.ServletOutputStream')
@provideSingleton(web.Server) @provideSingleton(web.Server)
export class Server { export class Server {
@@ -13,11 +19,14 @@ export class Server {
@JSClass('pw.yumc.MiaoScript.web.WebFilterProxy') @JSClass('pw.yumc.MiaoScript.web.WebFilterProxy')
private WebFilterProxy: any private WebFilterProxy: any
@inject(ContainerInstance)
private container: Container
private StreamUtils = org.springframework.util.StreamUtils private StreamUtils = org.springframework.util.StreamUtils
private ResponseEntity = org.springframework.http.ResponseEntity private ResponseEntity = org.springframework.http.ResponseEntity
private interceptors: Map<string, InterceptorAdapter> private interceptors: Map<string, InterceptorAdapter>
private handlerMapping: Map<string, RequestHandler> private methodMappings: Map<string, Map<string, RequestHandler>>
private beanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory private beanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory
@@ -25,7 +34,7 @@ export class Server {
initialization() { initialization() {
this.beanFactory = base.getInstance().getAutowireCapableBeanFactory() this.beanFactory = base.getInstance().getAutowireCapableBeanFactory()
this.interceptors = new Map() this.interceptors = new Map()
this.handlerMapping = new Map() this.methodMappings = new Map()
this.start() this.start()
} }
@@ -43,13 +52,77 @@ export class Server {
} }
} }
registryController(target: any) {
if (!target) { throw new Error('Controller can\'t be undefiend!') }
let controllerMetadata = getControllerMetadata(target)
if (!controllerMetadata) { throw new Error(`Controller ${target.name} must have @Controller decorator!`) }
try {
this.container.rebind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name)
} catch{
this.container.bind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name)
}
target = this.container.getNamed(METADATA_KEY.Controller, target.name)
let actions = getControllerActions(target)
for (const action of actions) {
let actionMetadata = getActionMetadata(target, action)
let path = `${controllerMetadata.path || ''}${actionMetadata.path || ''}`
if (!path) throw new Error(`Controller ${controllerMetadata.name} Action ${actionMetadata.name} path is empty!`)
if (!this.methodMappings.has(path)) { this.methodMappings.set(path, new Map()) }
console.debug(`Controller ${controllerMetadata.name} Registry ${path} to ${actionMetadata.executor || '<anonymous>'} Action function.`)
this.methodMappings.get(path).set(actionMetadata.method || 'ALL', (ctx: Context) => {
let args = []
let params = getActionParams(target, action)
for (const index in params) {
let param = params[index]
let paramValue = undefined
switch (param.type) {
case PARAM_TYPE.REQUEST: paramValue = ctx.request; break
case PARAM_TYPE.RESPONSE: paramValue = ctx.response; break
case PARAM_TYPE.QUERY: paramValue = ctx.params[param.name]; break
case PARAM_TYPE.HEADER: paramValue = ctx.headers[param.name]; break
case PARAM_TYPE.BODY: paramValue = ctx.body; break
case PARAM_TYPE.COOKIE: paramValue = ctx.cookies[param.name]; break
}
if (param.require && !paramValue) {
return {
status: 400,
msg: param.message ?? `Param Type ${param.type} require not empty!`,
data: param
}
}
args[param.index] = paramValue ?? param.default
}
return target[actionMetadata.executor].apply(target, args)
})
}
}
unregistryController(target: any) {
if (!target) { throw new Error('Controller can\'t be undefiend!') }
let controllerMetadata = getControllerMetadata(target)
if (!controllerMetadata) { throw new Error(`Controller ${target.name} must have @Controller decorator!`) }
try {
target = this.container.getNamed(METADATA_KEY.Controller, target.name)
} catch (error) {
throw new Error(`Controller ${target.name} not registry! err: ${error}`)
}
let actions = getControllerActions(target)
for (const action of actions) {
let actionMetadata = getActionMetadata(target, action)
let path = `${controllerMetadata.path || ''}${actionMetadata.path || ''}`
if (!this.methodMappings.has(path)) { continue }
this.methodMappings.get(path).delete(actionMetadata.method)
}
}
registryMapping(path: string, handler: RequestHandler) { registryMapping(path: string, handler: RequestHandler) {
console.debug(`Registry Mapping ${path} to handle ${handler.name || '<anonymous>'} function.`) console.debug(`Registry Mapping ${path} to handle ${handler.name || '<anonymous>'} function.`)
this.handlerMapping.set(path, handler) if (!this.methodMappings.has(path)) { this.methodMappings.set(path, new Map()) }
this.methodMappings.get(path).set("ALL", handler)
} }
unregistryMapping(path: string) { unregistryMapping(path: string) {
this.handlerMapping.delete(path) if (this.methodMappings.has(path)) { this.methodMappings.get(path).delete("ALL") }
} }
registryInterceptor(interceptor: InterceptorAdapter) { registryInterceptor(interceptor: InterceptorAdapter) {
@@ -65,28 +138,72 @@ export class Server {
try { this.beanFactory.destroySingleton(FilterProxyBeanName) } catch (ex) { } try { this.beanFactory.destroySingleton(FilterProxyBeanName) } catch (ex) { }
var WebFilterProxyNashorn = Java.extend(this.WebFilterProxy, { var WebFilterProxyNashorn = Java.extend(this.WebFilterProxy, {
doFilter: (servletRequest: javax.servlet.http.HttpServletRequest, servletResponse: javax.servlet.http.HttpServletResponse, filterChain: javax.servlet.FilterChain) => { doFilter: (servletRequest: javax.servlet.http.HttpServletRequest, servletResponse: javax.servlet.http.HttpServletResponse, filterChain: javax.servlet.FilterChain) => {
console.log('WebFilterProxyNashorn', 'doFilter', servletRequest, servletResponse)
filterChain.doFilter(servletRequest, servletResponse) filterChain.doFilter(servletRequest, servletResponse)
} }
}) })
this.beanFactory.registerSingleton(FilterProxyBeanName, new WebFilterProxyNashorn()) this.beanFactory.registerSingleton(FilterProxyBeanName, new WebFilterProxyNashorn())
} }
// private getRequestWrapper(servletRequest: javax.servlet.http.HttpServletRequest) {
// var body = org.springframework.util.StreamUtils.copyToByteArray(servletRequest.getInputStream())
// var HttpServletRequestWrapperAdapter = Java.extend(HttpServletRequestWrapper, {
// getInputStream: () => {
// var bais = new java.io.ByteArrayInputStream(body)
// return new ServletInputStream({
// read: () => bais.read(),
// isFinished: () => bais.available() == 0
// })
// }
// })
// var wrapper = new HttpServletRequestWrapperAdapter(servletRequest)
// return wrapper
// }
// private getResponseWrapper(servletResponse: javax.servlet.http.HttpServletResponse) {
// var HttpServletRequestWrapperAdapter = Java.extend(HttpServletRequestWrapper, {
// getOutputStream: () => {
// return new ServletOutputStream({
// })
// }
// })
// var wrapper = new HttpServletRequestWrapperAdapter(servletResponse)
// return wrapper
// }
private notFound(method: string, path: string) {
return {
status: 404,
msg: "handlerMapping Not Found!",
method,
path,
timestamp: Date.now()
}
}
private registryWebProxy() { private registryWebProxy() {
try { this.beanFactory.destroySingleton(WebProxyBeanName) } catch (ex) { } try { this.beanFactory.destroySingleton(WebProxyBeanName) } catch (ex) { }
var WebServerProxyNashorn = Java.extend(this.WebServerProxy, { var WebServerProxyNashorn = Java.extend(this.WebServerProxy, {
process: (req: javax.servlet.http.HttpServletRequest, resp: javax.servlet.http.HttpServletResponse) => { process: (req: javax.servlet.http.HttpServletRequest, resp: javax.servlet.http.HttpServletResponse) => {
let ctx: Context = { request: req, response: resp } let path = req.getRequestURI()
if (!this.methodMappings.has(path)) return this.notFound(req.getMethod(), path)
let mappings = this.methodMappings.get(req.getRequestURI())
let handler = mappings.get(req.getMethod()) || mappings.get("ALL")
if (!handler) return this.notFound(req.getMethod(), path)
let ctx: Context = { request: req, response: resp, params: {}, body: {}, handler }
ctx.url = req.getRequestURI() ctx.url = req.getRequestURI()
// @ts-ignore // @ts-ignore
ctx.header = { __noSuchProperty__: (name: string) => req.getHeader(name) + '' } ctx.headers = { __noSuchProperty__: (name: string) => req.getHeader(name) }
ctx.cookies = {}
for (const cookie of (req.getCookies() || [])) {
ctx.cookies[cookie.getName()] = cookie.getValue()
}
if (req.getQueryString()) { if (req.getQueryString()) {
ctx.url += `?${req.getQueryString()}` ctx.url += `?${req.getQueryString()}`
ctx.params = querystring.parse(req.getQueryString()) ctx.params = querystring.parse(req.getQueryString())
} }
if (req.getMethod() == "POST") { if (req.getMethod() == "POST") {
ctx.body = this.StreamUtils.copyToString(req.getInputStream(), java.nio.charset.StandardCharsets.UTF_8) ctx.body = this.StreamUtils.copyToString(req.getInputStream(), java.nio.charset.StandardCharsets.UTF_8)
if ((ctx.header['Content-Type'] || '').includes('application/json')) { if ((ctx.headers['Content-Type'] || '').includes('application/json')) {
try { try {
ctx.body = JSON.parse(ctx.body) ctx.body = JSON.parse(ctx.body)
} catch (error) { } catch (error) {
@@ -158,6 +275,7 @@ export class Server {
===================== MiaoSpring ===================== ===================== MiaoSpring =====================
Request Method : ${ctx.request.getMethod()} Request Method : ${ctx.request.getMethod()}
Request URL : ${ctx.url} Request URL : ${ctx.url}
Request Body : ${JSON.stringify(ctx.body)}
Response Body : ${JSON.stringify(Java.asJSONCompatible(ctx.result))} Response Body : ${JSON.stringify(Java.asJSONCompatible(ctx.result))}
Handle Time : ${Date.now() - startTime}ms Handle Time : ${Date.now() - startTime}ms
======================================================`) ======================================================`)
@@ -165,16 +283,8 @@ Handle Time : ${Date.now() - startTime}ms
} }
private execRequestHandle(ctx: Context) { private execRequestHandle(ctx: Context) {
if (!this.handlerMapping.has(ctx.request.getRequestURI())) {
return {
status: 404,
msg: "handlerMapping Not Found!",
path: ctx.url,
timestamp: Date.now()
}
}
try { try {
return this.handlerMapping.get(ctx.request.getRequestURI())(ctx) return ctx.handler(ctx)
} catch (error) { } catch (error) {
return { return {
status: 500, status: 500,

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/websocket", "name": "@ccms/websocket",
"version": "0.7.0", "version": "0.8.3",
"description": "MiaoScript websocket package", "description": "MiaoScript websocket package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,11 +19,10 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.8.3",
"@javatypes/tomcat-websocket-api": "^0.0.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.2" "typescript": "^3.9.6"
},
"dependencies": {
"@ccms/nashorn": "^0.7.0"
} }
} }

View File

@@ -1,4 +1,5 @@
/// <reference types="@ccms/nashorn" /> /// <reference types="@ccms/nashorn" />
/// <reference types="@javatypes/tomcat-websocket-api" />
import { Server, ServerOptions } from './socket-io' import { Server, ServerOptions } from './socket-io'
@@ -6,33 +7,33 @@ interface SocketIOStatic {
/** /**
* Default Server constructor * Default Server constructor
*/ */
(): Server; (): Server
/** /**
* Creates a new Server * Creates a new Server
* @param srv The HTTP server that we're going to bind to * @param srv The HTTP server that we're going to bind to
* @param opts An optional parameters object * @param opts An optional parameters object
*/ */
(srv: any, opts?: ServerOptions): Server; (srv: any, opts?: ServerOptions): Server
/** /**
* Creates a new Server * Creates a new Server
* @param port A port to bind to, as a number, or a string * @param port A port to bind to, as a number, or a string
* @param An optional parameters object * @param An optional parameters object
*/ */
(port: string | number, opts?: ServerOptions): Server; (port: string | number, opts?: ServerOptions): Server
/** /**
* Creates a new Server * Creates a new Server
* @param A parameters object * @param A parameters object
*/ */
(opts: ServerOptions): Server; (opts: ServerOptions): Server
/** /**
* Backwards compatibility * Backwards compatibility
* @see io().listen() * @see io().listen()
*/ */
listen?: SocketIOStatic; listen?: SocketIOStatic
} }
type SocketStatic = SocketIOStatic & { Instance?: symbol } type SocketStatic = SocketIOStatic & { Instance?: symbol }

View File

@@ -1,18 +1,18 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { ServerEvent } from './constants'; import { ServerEvent } from './constants'
import { Namespace } from './namespace'; import { Namespace } from './namespace'
import { Client } from './client'; import { Client } from './client'
import { SocketIO } from './interfaces' import { SocketIO } from './interfaces'
import { Parser } from './parser' import { Parser } from './parser'
import { PacketTypes, SubPacketTypes } from './types'; import { PacketTypes, SubPacketTypes } from './types'
import { Packet } from './packet'; import { Packet } from './packet'
import { Socket } from './socket'; import { Socket } from './socket'
import { Adapter } from './adapter'; import { Adapter } from './adapter'
interface ServerOptions extends SocketIO.ServerOptions { interface ServerOptions extends SocketIO.ServerOptions {
event?: EventEmitter; event?: EventEmitter
root?: string; root?: string
} }
interface WebSocketServer extends EventEmitter { interface WebSocketServer extends EventEmitter {
@@ -20,87 +20,87 @@ interface WebSocketServer extends EventEmitter {
} }
class Server implements SocketIO.Server { class Server implements SocketIO.Server {
private websocketServer: WebSocketServer; private websocketServer: WebSocketServer
private allClients: { [key: string]: Client }; private allClients: { [key: string]: Client }
engine: { ws: any; }; engine: { ws: any }
nsps: { [namespace: string]: Namespace; }; nsps: { [namespace: string]: Namespace }
sockets: Namespace; sockets: Namespace
json: SocketIO.Server; json: SocketIO.Server
volatile: SocketIO.Server; volatile: SocketIO.Server
local: SocketIO.Server; local: SocketIO.Server
parser = new Parser(); parser = new Parser();
_adapter: Adapter; _adapter: Adapter
options: ServerOptions; options: ServerOptions
constructor(instance: any, options: ServerOptions) { constructor(instance: any, options: ServerOptions) {
if (!instance) { throw new Error('instance can\'t be undefiend!') } if (!instance) { throw new Error('instance can\'t be undefiend!') }
this.allClients = {}; this.allClients = {}
this.nsps = {}; this.nsps = {}
this.sockets = new Namespace('/', this); this.sockets = new Namespace('/', this)
this.nsps['/'] = this.sockets; this.nsps['/'] = this.sockets
if (instance.class.name.startsWith('io.netty.channel')) { if (instance.class.name.startsWith('io.netty.channel')) {
let { NettyWebSocketServer } = require("../server") let { NettyWebSocketServer } = require("../server")
this.websocketServer = new NettyWebSocketServer(instance, Object.assign({ this.websocketServer = new NettyWebSocketServer(instance, Object.assign({
event: new EventEmitter(), event: new EventEmitter(),
path: '/socket.io', path: '/socket.io',
root: root + '/wwwroot' root: root + '/wwwroot'
}, options)); }, options))
} else { } else {
let { TomcatWebSocketServer } = require("../tomcat/server") let { TomcatWebSocketServer } = require("../tomcat/server")
this.websocketServer = new TomcatWebSocketServer(instance, options); this.websocketServer = new TomcatWebSocketServer(instance, options)
} }
this.initServer() this.initServer()
} }
checkRequest(req: any, fn: (err: any, success: boolean) => void): void { checkRequest(req: any, fn: (err: any, success: boolean) => void): void {
throw new Error("Method not implemented."); throw new Error("Method not implemented.")
} }
serveClient(): boolean; serveClient(): boolean
serveClient(v: boolean): SocketIO.Server; serveClient(v: boolean): SocketIO.Server
serveClient(v?: any): boolean | SocketIO.Server { serveClient(v?: any): boolean | SocketIO.Server {
throw new Error("Method not implemented."); throw new Error("Method not implemented.")
} }
path(): string; path(): string
path(v: string): SocketIO.Server; path(v: string): SocketIO.Server
path(v?: any): string | SocketIO.Server { path(v?: any): string | SocketIO.Server {
if (!arguments.length) return this.options.path; if (!arguments.length) return this.options.path
this.options.path = v.replace(/\/$/, ''); this.options.path = v.replace(/\/$/, '')
return this; return this
} }
adapter(): Adapter; adapter(): Adapter
adapter(v: any): SocketIO.Server; adapter(v: any): SocketIO.Server
adapter(v?: any): Adapter | SocketIO.Server { adapter(v?: any): Adapter | SocketIO.Server {
if (!arguments.length) return this._adapter; if (!arguments.length) return this._adapter
this._adapter = v; this._adapter = v
for (var i in this.nsps) { for (var i in this.nsps) {
if (this.nsps.hasOwnProperty(i)) { if (this.nsps.hasOwnProperty(i)) {
this.nsps[i].initAdapter(); this.nsps[i].initAdapter()
} }
} }
return this; return this
} }
origins(): string | string[]; origins(): string | string[]
origins(v: string | string[]): SocketIO.Server; origins(v: string | string[]): SocketIO.Server
origins(fn: (origin: string, callback: (error: string, success: boolean) => void) => void): SocketIO.Server; origins(fn: (origin: string, callback: (error: string, success: boolean) => void) => void): SocketIO.Server
origins(fn?: any): string | string[] | SocketIO.Server { origins(fn?: any): string | string[] | SocketIO.Server {
throw new Error("Method not implemented."); throw new Error("Method not implemented.")
} }
attach(srv: any, opts?: SocketIO.ServerOptions): SocketIO.Server; attach(srv: any, opts?: SocketIO.ServerOptions): SocketIO.Server
attach(port: number, opts?: SocketIO.ServerOptions): SocketIO.Server; attach(port: number, opts?: SocketIO.ServerOptions): SocketIO.Server
attach(port: any, opts?: any): SocketIO.Server { attach(port: any, opts?: any): SocketIO.Server {
throw new Error("Method not implemented."); throw new Error("Method not implemented.")
} }
listen(srv: any, opts?: SocketIO.ServerOptions): SocketIO.Server; listen(srv: any, opts?: SocketIO.ServerOptions): SocketIO.Server
listen(port: number, opts?: SocketIO.ServerOptions): SocketIO.Server; listen(port: number, opts?: SocketIO.ServerOptions): SocketIO.Server
listen(port: any, opts?: any): SocketIO.Server { listen(port: any, opts?: any): SocketIO.Server {
throw new Error("Method not implemented."); throw new Error("Method not implemented.")
} }
bind(srv: any): SocketIO.Server { bind(srv: any): SocketIO.Server {
throw new Error("Method not implemented."); throw new Error("Method not implemented.")
} }
onconnection(socket: Client): SocketIO.Server { onconnection(socket: Client): SocketIO.Server {
this.allClients[socket.id] = socket; this.allClients[socket.id] = socket
socket.packet({ socket.packet({
type: PacketTypes.OPEN, type: PacketTypes.OPEN,
data: { data: {
@@ -110,68 +110,75 @@ class Server implements SocketIO.Server {
pingTimeout: 5000 pingTimeout: 5000
} }
}) })
this.sockets.add(socket); this.sockets.add(socket)
return this; return this
} }
of(nsp: string): Namespace { of(nsp: string): Namespace {
if (!this.nsps[nsp]) { if (!this.nsps[nsp]) {
this.nsps[nsp] = new Namespace(nsp, this); this.nsps[nsp] = new Namespace(nsp, this)
} }
return this.nsps[nsp]; return this.nsps[nsp]
} }
close(fn?: () => void): void { close(fn?: () => void): void {
for (let socket in this.sockets.sockets) { for (let socket in this.sockets.sockets) {
this.sockets.sockets[socket].onclose() this.sockets.sockets[socket].onclose()
} }
this.websocketServer.close(); this.websocketServer.close()
} }
on(event: "connection", listener: (socket: SocketIO.Socket) => void): SocketIO.Namespace; on(event: "connection", listener: (socket: SocketIO.Socket) => void): SocketIO.Namespace
on(event: "connect", listener: (socket: SocketIO.Socket) => void): SocketIO.Namespace; on(event: "connect", listener: (socket: SocketIO.Socket) => void): SocketIO.Namespace
on(event: string, listener: Function): SocketIO.Namespace; on(event: string, listener: Function): SocketIO.Namespace
on(event: any, listener: any): SocketIO.Namespace { on(event: any, listener: any): SocketIO.Namespace {
return this.sockets.on(event, listener); return this.sockets.on(event, listener)
} }
to(room: string): SocketIO.Namespace { to(room: string): SocketIO.Namespace {
return this.sockets.to(room); return this.sockets.to(room)
} }
in(room: string): SocketIO.Namespace { in(room: string): SocketIO.Namespace {
return this.sockets.in(room); return this.sockets.in(room)
} }
use(fn: (socket: SocketIO.Socket, fn: (err?: any) => void) => void): SocketIO.Namespace { use(fn: (socket: SocketIO.Socket, fn: (err?: any) => void) => void): SocketIO.Namespace {
return this.sockets.use(fn); return this.sockets.use(fn)
} }
emit(event: string, ...args: any[]): SocketIO.Namespace { emit(event: string, ...args: any[]): SocketIO.Namespace {
// @ts-ignore // @ts-ignore
return this.sockets.emit(event, ...args); return this.sockets.emit(event, ...args)
} }
send(...args: any[]): SocketIO.Namespace { send(...args: any[]): SocketIO.Namespace {
return this.sockets.send(...args); return this.sockets.send(...args)
} }
write(...args: any[]): SocketIO.Namespace { write(...args: any[]): SocketIO.Namespace {
return this.sockets.write(...args); return this.sockets.write(...args)
} }
clients(...args: any[]): SocketIO.Namespace { clients(...args: any[]): SocketIO.Namespace {
return this.sockets.clients(args[0]); return this.sockets.clients(args[0])
} }
compress(...args: any[]): SocketIO.Namespace { compress(...args: any[]): SocketIO.Namespace {
return this.sockets.compress(args[0]) return this.sockets.compress(args[0])
} }
// =============================== // ===============================
checkNamespace(name, query, fn) { checkNamespace(name, query, fn) {
fn(false); fn(false)
}; };
private initServer() { private initServer() {
this.websocketServer.on(ServerEvent.connect, (socket: SocketIO.EngineSocket) => { this.websocketServer.on(ServerEvent.connect, (socket: SocketIO.EngineSocket) => {
let client = new Client(this, socket); let client = new Client(this, socket)
this.onconnection(client); this.onconnection(client)
}) })
this.websocketServer.on(ServerEvent.message, (socket: SocketIO.EngineSocket, text) => { this.websocketServer.on(ServerEvent.message, (socket: SocketIO.EngineSocket, text) => {
this.processPacket(this.parser.decode(text), this.allClients[socket.id]); this.processPacket(this.parser.decode(text), this.allClients[socket.id])
})
this.websocketServer.on(ServerEvent.disconnect, (socket: SocketIO.EngineSocket, reason) => {
this.allClients[socket.id].onclose(reason)
delete this.allClients[socket.id]
}) })
this.websocketServer.on(ServerEvent.error, (socket: SocketIO.EngineSocket, cause) => { this.websocketServer.on(ServerEvent.error, (socket: SocketIO.EngineSocket, cause) => {
console.error(`Client ${socket.id} cause error: ` + cause) if (socket.listeners(ServerEvent.error).length) {
console.ex(cause) socket.emit(ServerEvent.error, cause)
} else {
console.error(`client ${socket.id} cause error: ${cause}`)
}
}) })
} }
@@ -182,15 +189,15 @@ class Server implements SocketIO.Server {
type: PacketTypes.PONG, type: PacketTypes.PONG,
data: packet.data data: packet.data
}) })
break; break
case PacketTypes.UPGRADE: case PacketTypes.UPGRADE:
break; break
case PacketTypes.MESSAGE: case PacketTypes.MESSAGE:
this.processSubPacket(packet, client); this.processSubPacket(packet, client)
break; break
case PacketTypes.CLOSE: case PacketTypes.CLOSE:
client.onclose() client.onclose()
break; break
} }
} }
@@ -201,11 +208,11 @@ class Server implements SocketIO.Server {
type: PacketTypes.MESSAGE, type: PacketTypes.MESSAGE,
sub_type: SubPacketTypes.ERROR, sub_type: SubPacketTypes.ERROR,
data: 'not support dynamic namespace: ' + packet.nsp data: 'not support dynamic namespace: ' + packet.nsp
}); })
client.disconnect(); client.disconnect()
return; return
} }
namespace.process(packet, client); namespace.process(packet, client)
} }
} }
export { export {

View File

@@ -1,28 +1,29 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { SocketIO } from "./interfaces"; import { SocketIO } from "./interfaces"
import { Packet } from './packet'; import { Packet } from './packet'
import { PacketTypes, SubPacketTypes } from './types'; import { PacketTypes, SubPacketTypes } from './types'
import { Client } from './client'; import { Client } from './client'
import { Namespace } from './namespace'; import { Namespace } from './namespace'
import * as querystring from 'querystring' import * as querystring from 'querystring'
import { ServerEvent } from './constants'
export class Socket extends EventEmitter implements SocketIO.Socket { export class Socket extends EventEmitter implements SocketIO.Socket {
nsp: Namespace; nsp: Namespace
server: SocketIO.Server; server: SocketIO.Server
adapter: SocketIO.Adapter; adapter: SocketIO.Adapter
id: string; id: string
request: any; request: any
client: Client; client: Client
conn: SocketIO.EngineSocket; conn: SocketIO.EngineSocket
rooms: { [id: string]: string; }; rooms: { [id: string]: string }
acks: { [id: string]: Function; }; acks: { [id: string]: Function }
connected: boolean; connected: boolean
disconnected: boolean; disconnected: boolean
handshake: SocketIO.Handshake; handshake: SocketIO.Handshake
fns: any[]; fns: any[]
flags: { [key: string]: boolean }; flags: { [key: string]: boolean }
_rooms: string[]; _rooms: string[]
private events = [ private events = [
'error', 'error',
@@ -34,113 +35,113 @@ export class Socket extends EventEmitter implements SocketIO.Socket {
] ]
constructor(nsp: Namespace, client: Client, query = {}) { constructor(nsp: Namespace, client: Client, query = {}) {
super(); super()
this.nsp = nsp; this.nsp = nsp
this.server = nsp.server; this.server = nsp.server
this.adapter = this.nsp.adapter; this.adapter = this.nsp.adapter
this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id; this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id
this.client = client; this.client = client
this.request = client.request; this.request = client.request
this.conn = client.conn; this.conn = client.conn
this.rooms = {}; this.rooms = {}
this.acks = {}; this.acks = {}
this.connected = true; this.connected = true
this.disconnected = false; this.disconnected = false
this.handshake = this.buildHandshake(query); this.handshake = this.buildHandshake(query)
this.fns = []; this.fns = []
this.flags = {}; this.flags = {}
this._rooms = []; this._rooms = []
} }
get json() { get json() {
this.flags.json = true; this.flags.json = true
return this return this
} }
get volatile() { get volatile() {
this.flags.volatile = true; this.flags.volatile = true
return this return this
} }
get broadcast() { get broadcast() {
this.flags.broadcast = true; this.flags.broadcast = true
return this return this
} }
get local() { get local() {
this.flags.local = true; this.flags.local = true
return this return this
} }
to(room: string): SocketIO.Socket { to(room: string): SocketIO.Socket {
if (!~this._rooms.indexOf(room)) this._rooms.push(room); if (!~this._rooms.indexOf(room)) this._rooms.push(room)
return this; return this
} }
in(room: string): SocketIO.Socket { in(room: string): SocketIO.Socket {
return this.to(room); return this.to(room)
} }
use(fn: (packet: SocketIO.Packet, next: (err?: any) => void) => void): SocketIO.Socket { use(fn: (packet: SocketIO.Packet, next: (err?: any) => void) => void): SocketIO.Socket {
throw new Error("Method not implemented."); throw new Error("Method not implemented.")
} }
send(...args: any[]): SocketIO.Socket { send(...args: any[]): SocketIO.Socket {
this.emit("message", ...args) this.emit("message", ...args)
return this; return this
} }
write(...args: any[]): SocketIO.Socket { write(...args: any[]): SocketIO.Socket {
return this.send(...args); return this.send(...args)
} }
join(rooms: string | string[], fn?: (err?: any) => void): SocketIO.Socket { join(rooms: string | string[], fn?: (err?: any) => void): SocketIO.Socket {
if (!Array.isArray(rooms)) { if (!Array.isArray(rooms)) {
rooms = [rooms]; rooms = [rooms]
} }
rooms = rooms.filter((room) => { rooms = rooms.filter((room) => {
return !this.rooms.hasOwnProperty(room); return !this.rooms.hasOwnProperty(room)
}); })
if (!rooms.length) { if (!rooms.length) {
fn && fn(null); fn && fn(null)
return this; return this
} }
this.adapter.addAll(this.id, rooms, (err) => { this.adapter.addAll(this.id, rooms, (err) => {
if (err) return fn && fn(err); if (err) return fn && fn(err);
// debug('joined room %s', rooms); // debug('joined room %s', rooms);
(rooms as Array<string>).forEach((room) => { (rooms as Array<string>).forEach((room) => {
this.rooms[room] = room; this.rooms[room] = room
}); })
fn && fn(null); fn && fn(null)
}); })
return this; return this
} }
leave(name: string, fn?: Function): SocketIO.Socket { leave(name: string, fn?: Function): SocketIO.Socket {
delete this.rooms[name]; delete this.rooms[name]
fn && fn(null) fn && fn(null)
return this; return this
} }
leaveAll(): void { leaveAll(): void {
this.adapter.delAll(this.id); this.adapter.delAll(this.id)
this.rooms = {}; this.rooms = {}
} }
disconnect(close?: boolean): SocketIO.Socket { disconnect(close?: boolean): SocketIO.Socket {
if (!this.connected) return this; if (!this.connected) return this
if (close) { if (close) {
this.client.disconnect(); this.client.disconnect()
} else { } else {
this.packet({ type: PacketTypes.MESSAGE, sub_type: SubPacketTypes.DISCONNECT }); this.packet({ type: PacketTypes.MESSAGE, sub_type: SubPacketTypes.DISCONNECT })
this.onclose('server namespace disconnect'); this.onclose('server namespace disconnect')
} }
return this; return this
} }
compress(compress: boolean): SocketIO.Socket { compress(compress: boolean): SocketIO.Socket {
throw new Error("Method not implemented."); throw new Error("Method not implemented.")
} }
error(err: any): void { error(err: any): void {
this.packet({ type: PacketTypes.MESSAGE, sub_type: SubPacketTypes.ERROR, data: err }); this.packet({ type: PacketTypes.MESSAGE, sub_type: SubPacketTypes.ERROR, data: err })
} }
// ========================================== // ==========================================
buildHandshake(query): SocketIO.Handshake { buildHandshake(query): SocketIO.Handshake {
let requestUri = this.request.uri(); let requestUri = this.request.uri()
let headers = {}; let headers = {}
let nativeHeaders = this.request.headers(); let nativeHeaders = this.request.headers()
nativeHeaders.forEach(function (header) { nativeHeaders.forEach(function (header) {
headers[header.getKey()] = header.getValue(); headers[header.getKey()] = header.getValue()
}) })
return { return {
headers: headers, headers: headers,
@@ -155,9 +156,9 @@ export class Socket extends EventEmitter implements SocketIO.Socket {
} }
emit(event: string, ...args: any[]): boolean { emit(event: string, ...args: any[]): boolean {
if (~this.events.indexOf(event)) { if (~this.events.indexOf(event)) {
super.emit(event, ...args); super.emit(event, ...args)
// @ts-ignore // @ts-ignore
return this; return this
} }
let packet: Packet = { let packet: Packet = {
@@ -170,44 +171,48 @@ export class Socket extends EventEmitter implements SocketIO.Socket {
// access last argument to see if it's an ACK callback // access last argument to see if it's an ACK callback
if (typeof args[args.length - 1] === 'function') { if (typeof args[args.length - 1] === 'function') {
if (this._rooms.length || this.flags.broadcast) { if (this._rooms.length || this.flags.broadcast) {
throw new Error('Callbacks are not supported when broadcasting'); throw new Error('Callbacks are not supported when broadcasting')
} }
// debug('emitting packet with ack id %d', this.nsp.ids); // debug('emitting packet with ack id %d', this.nsp.ids);
this.acks[this.nsp.ids] = args.pop(); this.acks[this.nsp.ids] = args.pop()
packet.id = this.nsp.ids++; packet.id = this.nsp.ids++
} }
let rooms = this._rooms.slice(0); let rooms = this._rooms.slice(0)
let flags = Object.assign({}, this.flags); let flags = Object.assign({}, this.flags)
// reset flags // reset flags
this._rooms = []; this._rooms = []
this.flags = {}; this.flags = {}
if (rooms.length || flags.broadcast) { if (rooms.length || flags.broadcast) {
this.adapter.broadcast(packet, { this.adapter.broadcast(packet, {
except: [this.id], except: [this.id],
rooms: rooms, rooms: rooms,
flags: flags flags: flags
}); })
} else { } else {
// dispatch packet // dispatch packet
this.packet(packet, flags); this.packet(packet, flags)
} }
// @ts-ignore // @ts-ignore
return this; return this
} }
packet(packet: Packet, opts: any = { preEncoded: false }) { packet(packet: Packet, opts: any = { preEncoded: false }) {
if (!opts.preEncoded) { if (!opts.preEncoded) {
packet.nsp = this.nsp.name; packet.nsp = this.nsp.name
opts.compress = false !== opts.compress; opts.compress = false !== opts.compress
}
try {
this.client.packet(packet, opts)
} catch (error) {
this.onerror(error)
} }
this.client.packet(packet, opts);
} }
onconnect() { onconnect() {
this.nsp.connected[this.id] = this; this.nsp.connected[this.id] = this
this.client.sockets[this.id] = this; this.client.sockets[this.id] = this
this.join(this.id); this.join(this.id)
// let skip = this.nsp.name === '/' && this.nsp.fns.length === 0; // let skip = this.nsp.name === '/' && this.nsp.fns.length === 0;
// if (skip) { // if (skip) {
// debug('packet already sent in initial handshake'); // debug('packet already sent in initial handshake');
@@ -215,54 +220,54 @@ export class Socket extends EventEmitter implements SocketIO.Socket {
this.packet({ this.packet({
type: PacketTypes.MESSAGE, type: PacketTypes.MESSAGE,
sub_type: SubPacketTypes.CONNECT sub_type: SubPacketTypes.CONNECT
}); })
// } // }
} }
onclose(reason?: string) { onclose(reason?: string) {
if (!this.connected) return this; if (!this.connected) return this
// debug('closing socket - reason %s', reason); // debug('closing socket - reason %s', reason);
this.emit('disconnecting', reason); this.emit('disconnecting', reason)
this.leaveAll(); this.leaveAll()
this.nsp.remove(this); this.nsp.remove(this)
this.client.remove(this); this.client.remove(this)
this.connected = false; this.connected = false
this.disconnected = true; this.disconnected = true
delete this.nsp.connected[this.id]; delete this.nsp.connected[this.id]
this.emit('disconnect', reason); this.emit('disconnect', reason)
} }
onpacket(packet: Packet) { onpacket(packet: Packet) {
switch (packet.sub_type) { switch (packet.sub_type) {
// 2 // 2
case SubPacketTypes.EVENT: case SubPacketTypes.EVENT:
this.onevent(packet); this.onevent(packet)
break; break
// 5 // 5
case SubPacketTypes.BINARY_EVENT: case SubPacketTypes.BINARY_EVENT:
this.onevent(packet); this.onevent(packet)
break; break
// 3 // 3
case SubPacketTypes.ACK: case SubPacketTypes.ACK:
this.onack(packet); this.onack(packet)
break; break
// 6 // 6
case SubPacketTypes.BINARY_ACK: case SubPacketTypes.BINARY_ACK:
this.onack(packet); this.onack(packet)
break; break
// 1 // 1
case SubPacketTypes.DISCONNECT: case SubPacketTypes.DISCONNECT:
this.ondisconnect(); this.ondisconnect()
break; break
// 4 // 4
case SubPacketTypes.ERROR: case SubPacketTypes.ERROR:
this.onerror(new Error(packet.data)); this.onerror(new Error(packet.data))
} }
} }
onerror(err: Error) { onerror(err: Error) {
if (this.listeners('error').length) { if (this.listeners('error').length) {
this.emit('error', err); this.emit('error', err)
} else { } else {
console.error('Missing error handler on `socket`.'); console.error('Missing error handler on `socket`.')
console.error(err.stack); console.error(err.stack)
} }
} }
ondisconnect() { ondisconnect() {
@@ -271,39 +276,39 @@ export class Socket extends EventEmitter implements SocketIO.Socket {
onevent(packet: Packet) { onevent(packet: Packet) {
if (null != packet.id) { if (null != packet.id) {
// debug('attaching ack callback to event'); // debug('attaching ack callback to event');
this.dispatch(packet, this.ack(packet.id)); this.dispatch(packet, this.ack(packet.id))
} else { } else {
this.dispatch(packet); this.dispatch(packet)
} }
} }
ack(id: number) { ack(id: number) {
let sent = false; let sent = false
return (...args: any[]) => { return (...args: any[]) => {
if (sent) return; if (sent) return
this.packet({ this.packet({
id: id, id: id,
type: PacketTypes.MESSAGE, type: PacketTypes.MESSAGE,
sub_type: this.hasBin(args) ? SubPacketTypes.BINARY_ACK : SubPacketTypes.ACK, sub_type: this.hasBin(args) ? SubPacketTypes.BINARY_ACK : SubPacketTypes.ACK,
data: args data: args
}); })
sent = true; sent = true
} }
} }
onack(packet: Packet) { onack(packet: Packet) {
let ack = this.acks[packet.id]; let ack = this.acks[packet.id]
if ('function' == typeof ack) { if ('function' == typeof ack) {
// debug('calling ack %s with %j', packet.id, packet.data); // debug('calling ack %s with %j', packet.id, packet.data);
ack.apply(this, packet.data); ack.apply(this, packet.data)
delete this.acks[packet.id]; delete this.acks[packet.id]
} else { } else {
// debug('bad ack %s', packet.id); // debug('bad ack %s', packet.id);
} }
} }
dispatch(packet: Packet, ack?: Function) { dispatch(packet: Packet, ack?: Function) {
if (ack) { this.acks[packet.id] = ack; } if (ack) { this.acks[packet.id] = ack }
super.emit(packet.name, ...packet.data, ack) super.emit(packet.name, ...packet.data, ack)
} }
private hasBin(obj: any) { private hasBin(obj: any) {
return false; return false
} }
} }

View File

@@ -1,44 +1,45 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { SocketIO } from '../socket-io/interfaces'; import { SocketIO } from '../socket-io/interfaces'
export class TomcatClient extends EventEmitter implements SocketIO.EngineSocket { export class TomcatClient extends EventEmitter implements SocketIO.EngineSocket {
private _id: string; private _id: string
private session: any private session: javax.websocket.Session
server: any; server: any
readyState: string; readyState: string
remoteAddress: string; remoteAddress: string
upgraded: boolean; upgraded: boolean
request: any; request: any
transport: any; transport: any
constructor(server: any, session: any) { constructor(server: any, session: javax.websocket.Session) {
super(); super()
this.server = server; this.server = server
this.readyState = 'open'; this.readyState = 'open'
this.remoteAddress = session + '' this.remoteAddress = session + ''
this.upgraded = true; this.upgraded = true
this.request = { this.request = {
uri: () => { uri: () => `${session.getRequestURI()}`,
return session.getRequestURI() + '' headers: () => []
},
headers: () => {
return []
} }
}; this.transport = null
this.transport = null;
this.session = session; this.session = session
this._id = session.getId(); this._id = session.getId()
} }
get id() { get id() {
return this._id; return this._id
} }
send(text: string) { send(text: string) {
if (this.readyState == 'open') {
Java.synchronized(() => this.session.getBasicRemote().sendText(text), this.session)() Java.synchronized(() => this.session.getBasicRemote().sendText(text), this.session)()
} }
}
close() { close() {
this.session.close(); if (this.readyState == 'open') {
this.readyState = 'close'
this.session.close()
}
} }
} }

View File

@@ -10,9 +10,7 @@ const WebSocketServerProxy = Java.type("pw.yumc.MiaoScript.websocket.WebSocketPr
const ThreadPoolExecutor = Java.type('java.util.concurrent.ThreadPoolExecutor') const ThreadPoolExecutor = Java.type('java.util.concurrent.ThreadPoolExecutor')
const ThreadPoolTaskExecutor = Java.type('org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor') const ThreadPoolTaskExecutor = Java.type('org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor')
interface TomcatWebSocketSession { type TomcatWebSocketSession = javax.websocket.Session
getId: () => number
}
class TomcatWebSocketServer extends EventEmitter { class TomcatWebSocketServer extends EventEmitter {
private beanFactory: any private beanFactory: any

View File

@@ -7,6 +7,7 @@
"module": "commonjs", "module": "commonjs",
"strict": true, "strict": true,
"strictNullChecks": false, "strictNullChecks": false,
"importHelpers": true,
"sourceMap": true, "sourceMap": true,
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,