[add] 第一版完成
This commit is contained in:
		
							
								
								
									
										8
									
								
								.env.dev
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								.env.dev
									
									
									
									
									
								
							@@ -1,5 +1,13 @@
 | 
				
			|||||||
URLPATH = uploads
 | 
					URLPATH = uploads
 | 
				
			||||||
PORT = 3101
 | 
					PORT = 3101
 | 
				
			||||||
 | 
					LINE_ACCESS_TOKEN = lbXrLMGoiOvBTG3vFNlVNQbolcVkQzqi920DVwrbtr1
 | 
				
			||||||
prikeyPath = ./certificate/RSA-privkey.pem
 | 
					prikeyPath = ./certificate/RSA-privkey.pem
 | 
				
			||||||
certPath = ./certificate/RSA-cert.pem
 | 
					certPath = ./certificate/RSA-cert.pem
 | 
				
			||||||
cafilePath = ./certificate/RSA-chain.pem
 | 
					cafilePath = ./certificate/RSA-chain.pem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# DB----------------------------------------------
 | 
				
			||||||
 | 
					DB_HOST = 192.168.0.15
 | 
				
			||||||
 | 
					DB_PORT = 3307
 | 
				
			||||||
 | 
					DB_USER = jianmiau
 | 
				
			||||||
 | 
					DB_PASSWORD = VQ*ZetC7xcc9%dTW
 | 
				
			||||||
 | 
					DB_DATABASE = badminton
 | 
				
			||||||
@@ -1,5 +1,13 @@
 | 
				
			|||||||
URLPATH = uploads
 | 
					URLPATH = uploads
 | 
				
			||||||
PORT = 3100
 | 
					PORT = 3100
 | 
				
			||||||
 | 
					LINE_ACCESS_TOKEN = lbXrLMGoiOvBTG3vFNlVNQbolcVkQzqi920DVwrbtr1
 | 
				
			||||||
prikeyPath = /certificate/RSA-privkey.pem
 | 
					prikeyPath = /certificate/RSA-privkey.pem
 | 
				
			||||||
certPath = /certificate/RSA-cert.pem
 | 
					certPath = /certificate/RSA-cert.pem
 | 
				
			||||||
cafilePath = /certificate/RSA-chain.pem
 | 
					cafilePath = /certificate/RSA-chain.pem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# DB----------------------------------------------
 | 
				
			||||||
 | 
					DB_HOST = 192.168.0.15
 | 
				
			||||||
 | 
					DB_PORT = 3307
 | 
				
			||||||
 | 
					DB_USER = jianmiau
 | 
				
			||||||
 | 
					DB_PASSWORD = VQ*ZetC7xcc9%dTW
 | 
				
			||||||
 | 
					DB_DATABASE = badminton
 | 
				
			||||||
							
								
								
									
										21
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						// 使用 IntelliSense 以得知可用的屬性。
 | 
				
			||||||
 | 
						// 暫留以檢視現有屬性的描述。
 | 
				
			||||||
 | 
						// 如需詳細資訊,請瀏覽: https://go.microsoft.com/fwlink/?linkid=830387
 | 
				
			||||||
 | 
						"version": "0.2.0",
 | 
				
			||||||
 | 
						"configurations": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"type": "node",
 | 
				
			||||||
 | 
								"request": "attach",
 | 
				
			||||||
 | 
								"name": "Attach to Remote",
 | 
				
			||||||
 | 
								"address": "jianmiau.tk:9229/87f42d5b-97bf-4d25-a4d7-37306985459a",
 | 
				
			||||||
 | 
								"port": 9229,
 | 
				
			||||||
 | 
								"localRoot": "${workspaceFolder}",
 | 
				
			||||||
 | 
								// "remoteRoot": "W:/home/www/api",
 | 
				
			||||||
 | 
								"remoteRoot": "/volume1/homes/JianMiau/www/api",
 | 
				
			||||||
 | 
								"skipFiles": [
 | 
				
			||||||
 | 
									"<node_internals>/**"
 | 
				
			||||||
 | 
								]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										44
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								Dockerfile
									
									
									
									
									
								
							@@ -1,24 +1,19 @@
 | 
				
			|||||||
# sudo docker build -t linebotts .
 | 
					# sudo docker build -t api .
 | 
				
			||||||
 | 
					 | 
				
			||||||
# 将 Docker 镜像保存为文件
 | 
					 | 
				
			||||||
# sudo docker save -o linebotts.tar linebotts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 使用 scp 传输文件到目标机器
 | 
					 | 
				
			||||||
# scp -P 20022 linebotts.tar jianmiau@192.168.0.77:~/Nas/docker
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 在目标机器上加载镜像
 | 
					 | 
				
			||||||
# sudo docker load -i /path/to/destination/linebotts.tar
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# sudo docker run -v /volume1/homes/JianMiau/www/certificate:/certificate -e TZ=Asia/Taipei --name=linebotts --restart always --net=host linebotts
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 後續查看容器
 | 
					# 後續查看容器
 | 
				
			||||||
# docker ps
 | 
					# docker ps
 | 
				
			||||||
# sudo docker exec -it [Container ID] /bin/bash
 | 
					# sudo docker exec -it [Container ID] /bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 選擇node
 | 
					# 選擇node
 | 
				
			||||||
FROM node:19.4.0
 | 
					FROM node:20.9.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 安装系统依赖
 | 
				
			||||||
 | 
					RUN apt-get update && apt-get install -y \
 | 
				
			||||||
 | 
					    libcairo2-dev \
 | 
				
			||||||
 | 
					    libjpeg-dev \
 | 
				
			||||||
 | 
					    libpango1.0-dev \
 | 
				
			||||||
 | 
					    libgif-dev \
 | 
				
			||||||
 | 
					    build-essential
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 指定NODE_ENV為production
 | 
					# 指定NODE_ENV為production
 | 
				
			||||||
ENV NODE_ENV=production
 | 
					ENV NODE_ENV=production
 | 
				
			||||||
@@ -28,8 +23,8 @@ WORKDIR /app
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
VOLUME ["/certificate"]
 | 
					VOLUME ["/certificate"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 只copy package.json檔案
 | 
					# 复制 package.json
 | 
				
			||||||
COPY ["package.json", "./"]
 | 
					COPY package.json ./
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 安裝dependencies
 | 
					# 安裝dependencies
 | 
				
			||||||
# If you are building your code for production
 | 
					# If you are building your code for production
 | 
				
			||||||
@@ -41,3 +36,18 @@ COPY . .
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# 指定啟動container後執行命令
 | 
					# 指定啟動container後執行命令
 | 
				
			||||||
CMD [ "npm", "start" ]
 | 
					CMD [ "npm", "start" ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 将 Docker 镜像保存为文件
 | 
				
			||||||
 | 
					# sudo docker save -o api.tar api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 使用 scp 传输文件到目标机器
 | 
				
			||||||
 | 
					# scp -P 20022 api.tar jianmiau@192.168.0.77:~/Nas/docker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 在目标机器上加载镜像
 | 
				
			||||||
 | 
					# sudo docker load -i /path/to/destination/api.tar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# sudo docker run -v /volume1/homes/JianMiau/www/certificate:/certificate -e TZ=Asia/Taipei --name=api --restart always --net=host api
 | 
				
			||||||
							
								
								
									
										308
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										308
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,22 +1,26 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "canvas",
 | 
						"name": "api",
 | 
				
			||||||
	"version": "1.0.0",
 | 
						"version": "1.0.0",
 | 
				
			||||||
	"lockfileVersion": 3,
 | 
						"lockfileVersion": 3,
 | 
				
			||||||
	"requires": true,
 | 
						"requires": true,
 | 
				
			||||||
	"packages": {
 | 
						"packages": {
 | 
				
			||||||
		"": {
 | 
							"": {
 | 
				
			||||||
			"name": "canvas",
 | 
								"name": "api",
 | 
				
			||||||
			"version": "1.0.0",
 | 
								"version": "1.0.0",
 | 
				
			||||||
			"license": "ISC",
 | 
								"license": "ISC",
 | 
				
			||||||
			"dependencies": {
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"axios": "^1.7.7",
 | 
				
			||||||
				"canvas": "^2.11.2",
 | 
									"canvas": "^2.11.2",
 | 
				
			||||||
				"dateformat": "^4.5.1",
 | 
									"dateformat": "^4.5.1",
 | 
				
			||||||
				"dayjs": "^1.11.7",
 | 
									"dayjs": "^1.11.7",
 | 
				
			||||||
				"dotenv": "^16.0.3",
 | 
									"dotenv": "^16.0.3",
 | 
				
			||||||
				"express": "^4.20.0",
 | 
									"express": "^4.20.0",
 | 
				
			||||||
 | 
									"form-data": "^4.0.0",
 | 
				
			||||||
				"fs": "^0.0.1-security",
 | 
									"fs": "^0.0.1-security",
 | 
				
			||||||
				"multer": "^1.4.5-lts.1",
 | 
									"multer": "^1.4.5-lts.1",
 | 
				
			||||||
				"nodemon": "^3.1.4"
 | 
									"mysql": "^2.18.1",
 | 
				
			||||||
 | 
									"nodemon": "^3.1.4",
 | 
				
			||||||
 | 
									"ts-node": "^10.9.2"
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			"devDependencies": {
 | 
								"devDependencies": {
 | 
				
			||||||
				"@types/dateformat": "^5.0.0",
 | 
									"@types/dateformat": "^5.0.0",
 | 
				
			||||||
@@ -25,6 +29,39 @@
 | 
				
			|||||||
				"@types/node": "^22.5.4"
 | 
									"@types/node": "^22.5.4"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/@cspotcode/source-map-support": {
 | 
				
			||||||
 | 
								"version": "0.8.1",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"@jridgewell/trace-mapping": "0.3.9"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">=12"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/@jridgewell/resolve-uri": {
 | 
				
			||||||
 | 
								"version": "3.1.2",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">=6.0.0"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/@jridgewell/sourcemap-codec": {
 | 
				
			||||||
 | 
								"version": "1.5.0",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/@jridgewell/trace-mapping": {
 | 
				
			||||||
 | 
								"version": "0.3.9",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"@jridgewell/resolve-uri": "^3.0.3",
 | 
				
			||||||
 | 
									"@jridgewell/sourcemap-codec": "^1.4.10"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/@mapbox/node-pre-gyp": {
 | 
							"node_modules/@mapbox/node-pre-gyp": {
 | 
				
			||||||
			"version": "1.0.11",
 | 
								"version": "1.0.11",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
 | 
								"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
 | 
				
			||||||
@@ -44,6 +81,26 @@
 | 
				
			|||||||
				"node-pre-gyp": "bin/node-pre-gyp"
 | 
									"node-pre-gyp": "bin/node-pre-gyp"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/@tsconfig/node10": {
 | 
				
			||||||
 | 
								"version": "1.0.11",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/@tsconfig/node12": {
 | 
				
			||||||
 | 
								"version": "1.0.11",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/@tsconfig/node14": {
 | 
				
			||||||
 | 
								"version": "1.0.3",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/@tsconfig/node16": {
 | 
				
			||||||
 | 
								"version": "1.0.4",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/@types/body-parser": {
 | 
							"node_modules/@types/body-parser": {
 | 
				
			||||||
			"version": "1.19.5",
 | 
								"version": "1.19.5",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
 | 
								"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
 | 
				
			||||||
@@ -118,7 +175,6 @@
 | 
				
			|||||||
			"version": "22.5.4",
 | 
								"version": "22.5.4",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
 | 
								"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
 | 
				
			||||||
			"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
 | 
								"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
 | 
				
			||||||
			"dev": true,
 | 
					 | 
				
			||||||
			"dependencies": {
 | 
								"dependencies": {
 | 
				
			||||||
				"undici-types": "~6.19.2"
 | 
									"undici-types": "~6.19.2"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -173,6 +229,28 @@
 | 
				
			|||||||
				"node": ">= 0.6"
 | 
									"node": ">= 0.6"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/acorn": {
 | 
				
			||||||
 | 
								"version": "8.12.1",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
 | 
				
			||||||
 | 
								"bin": {
 | 
				
			||||||
 | 
									"acorn": "bin/acorn"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">=0.4.0"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/acorn-walk": {
 | 
				
			||||||
 | 
								"version": "8.3.4",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"acorn": "^8.11.0"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">=0.4.0"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/agent-base": {
 | 
							"node_modules/agent-base": {
 | 
				
			||||||
			"version": "6.0.2",
 | 
								"version": "6.0.2",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
 | 
								"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
 | 
				
			||||||
@@ -227,16 +305,44 @@
 | 
				
			|||||||
				"node": ">=10"
 | 
									"node": ">=10"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/arg": {
 | 
				
			||||||
 | 
								"version": "4.1.3",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/array-flatten": {
 | 
							"node_modules/array-flatten": {
 | 
				
			||||||
			"version": "1.1.1",
 | 
								"version": "1.1.1",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
 | 
								"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
 | 
				
			||||||
			"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
 | 
								"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/asynckit": {
 | 
				
			||||||
 | 
								"version": "0.4.0",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/axios": {
 | 
				
			||||||
 | 
								"version": "1.7.7",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"follow-redirects": "^1.15.6",
 | 
				
			||||||
 | 
									"form-data": "^4.0.0",
 | 
				
			||||||
 | 
									"proxy-from-env": "^1.1.0"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/balanced-match": {
 | 
							"node_modules/balanced-match": {
 | 
				
			||||||
			"version": "1.0.2",
 | 
								"version": "1.0.2",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
 | 
								"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
 | 
				
			||||||
			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
 | 
								"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/bignumber.js": {
 | 
				
			||||||
 | 
								"version": "9.0.0",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": "*"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/binary-extensions": {
 | 
							"node_modules/binary-extensions": {
 | 
				
			||||||
			"version": "2.3.0",
 | 
								"version": "2.3.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
 | 
				
			||||||
@@ -413,6 +519,17 @@
 | 
				
			|||||||
				"color-support": "bin.js"
 | 
									"color-support": "bin.js"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/combined-stream": {
 | 
				
			||||||
 | 
								"version": "1.0.8",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"delayed-stream": "~1.0.0"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">= 0.8"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/concat-map": {
 | 
							"node_modules/concat-map": {
 | 
				
			||||||
			"version": "0.0.1",
 | 
								"version": "0.0.1",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
 | 
								"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
 | 
				
			||||||
@@ -501,6 +618,11 @@
 | 
				
			|||||||
			"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
 | 
								"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
 | 
				
			||||||
			"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
 | 
								"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/create-require": {
 | 
				
			||||||
 | 
								"version": "1.1.1",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/dateformat": {
 | 
							"node_modules/dateformat": {
 | 
				
			||||||
			"version": "4.6.3",
 | 
								"version": "4.6.3",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
 | 
								"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
 | 
				
			||||||
@@ -557,6 +679,14 @@
 | 
				
			|||||||
				"url": "https://github.com/sponsors/ljharb"
 | 
									"url": "https://github.com/sponsors/ljharb"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/delayed-stream": {
 | 
				
			||||||
 | 
								"version": "1.0.0",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">=0.4.0"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/delegates": {
 | 
							"node_modules/delegates": {
 | 
				
			||||||
			"version": "1.0.0",
 | 
								"version": "1.0.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
 | 
				
			||||||
@@ -587,6 +717,14 @@
 | 
				
			|||||||
				"node": ">=8"
 | 
									"node": ">=8"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/diff": {
 | 
				
			||||||
 | 
								"version": "4.0.2",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">=0.3.1"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/dotenv": {
 | 
							"node_modules/dotenv": {
 | 
				
			||||||
			"version": "16.4.5",
 | 
								"version": "16.4.5",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
 | 
								"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
 | 
				
			||||||
@@ -751,6 +889,38 @@
 | 
				
			|||||||
			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
 | 
				
			||||||
			"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
 | 
								"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/follow-redirects": {
 | 
				
			||||||
 | 
								"version": "1.15.9",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
 | 
				
			||||||
 | 
								"funding": [
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"type": "individual",
 | 
				
			||||||
 | 
										"url": "https://github.com/sponsors/RubenVerborgh"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								],
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">=4.0"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"peerDependenciesMeta": {
 | 
				
			||||||
 | 
									"debug": {
 | 
				
			||||||
 | 
										"optional": true
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/form-data": {
 | 
				
			||||||
 | 
								"version": "4.0.0",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"asynckit": "^0.4.0",
 | 
				
			||||||
 | 
									"combined-stream": "^1.0.8",
 | 
				
			||||||
 | 
									"mime-types": "^2.1.12"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">= 6"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/forwarded": {
 | 
							"node_modules/forwarded": {
 | 
				
			||||||
			"version": "0.2.0",
 | 
								"version": "0.2.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
 | 
				
			||||||
@@ -1096,6 +1266,11 @@
 | 
				
			|||||||
				"semver": "bin/semver.js"
 | 
									"semver": "bin/semver.js"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/make-error": {
 | 
				
			||||||
 | 
								"version": "1.3.6",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/media-typer": {
 | 
							"node_modules/media-typer": {
 | 
				
			||||||
			"version": "0.3.0",
 | 
								"version": "0.3.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
 | 
				
			||||||
@@ -1255,6 +1430,55 @@
 | 
				
			|||||||
				"mkdirp": "bin/cmd.js"
 | 
									"mkdirp": "bin/cmd.js"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/mysql": {
 | 
				
			||||||
 | 
								"version": "2.18.1",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"bignumber.js": "9.0.0",
 | 
				
			||||||
 | 
									"readable-stream": "2.3.7",
 | 
				
			||||||
 | 
									"safe-buffer": "5.1.2",
 | 
				
			||||||
 | 
									"sqlstring": "2.3.1"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">= 0.6"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/mysql/node_modules/readable-stream": {
 | 
				
			||||||
 | 
								"version": "2.3.7",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"core-util-is": "~1.0.0",
 | 
				
			||||||
 | 
									"inherits": "~2.0.3",
 | 
				
			||||||
 | 
									"isarray": "~1.0.0",
 | 
				
			||||||
 | 
									"process-nextick-args": "~2.0.0",
 | 
				
			||||||
 | 
									"safe-buffer": "~5.1.1",
 | 
				
			||||||
 | 
									"string_decoder": "~1.1.1",
 | 
				
			||||||
 | 
									"util-deprecate": "~1.0.1"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/mysql/node_modules/safe-buffer": {
 | 
				
			||||||
 | 
								"version": "5.1.2",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/mysql/node_modules/sqlstring": {
 | 
				
			||||||
 | 
								"version": "2.3.1",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==",
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">= 0.6"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/mysql/node_modules/string_decoder": {
 | 
				
			||||||
 | 
								"version": "1.1.1",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"safe-buffer": "~5.1.0"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/nan": {
 | 
							"node_modules/nan": {
 | 
				
			||||||
			"version": "2.20.0",
 | 
								"version": "2.20.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
 | 
				
			||||||
@@ -1435,6 +1659,11 @@
 | 
				
			|||||||
				"node": ">= 0.10"
 | 
									"node": ">= 0.10"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/proxy-from-env": {
 | 
				
			||||||
 | 
								"version": "1.1.0",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/pstree.remy": {
 | 
							"node_modules/pstree.remy": {
 | 
				
			||||||
			"version": "1.1.8",
 | 
								"version": "1.1.8",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
 | 
								"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
 | 
				
			||||||
@@ -1847,6 +2076,48 @@
 | 
				
			|||||||
			"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
 | 
								"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
 | 
				
			||||||
			"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
 | 
								"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/ts-node": {
 | 
				
			||||||
 | 
								"version": "10.9.2",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
 | 
				
			||||||
 | 
								"dependencies": {
 | 
				
			||||||
 | 
									"@cspotcode/source-map-support": "^0.8.0",
 | 
				
			||||||
 | 
									"@tsconfig/node10": "^1.0.7",
 | 
				
			||||||
 | 
									"@tsconfig/node12": "^1.0.7",
 | 
				
			||||||
 | 
									"@tsconfig/node14": "^1.0.0",
 | 
				
			||||||
 | 
									"@tsconfig/node16": "^1.0.2",
 | 
				
			||||||
 | 
									"acorn": "^8.4.1",
 | 
				
			||||||
 | 
									"acorn-walk": "^8.1.1",
 | 
				
			||||||
 | 
									"arg": "^4.1.0",
 | 
				
			||||||
 | 
									"create-require": "^1.1.0",
 | 
				
			||||||
 | 
									"diff": "^4.0.1",
 | 
				
			||||||
 | 
									"make-error": "^1.1.1",
 | 
				
			||||||
 | 
									"v8-compile-cache-lib": "^3.0.1",
 | 
				
			||||||
 | 
									"yn": "3.1.1"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"bin": {
 | 
				
			||||||
 | 
									"ts-node": "dist/bin.js",
 | 
				
			||||||
 | 
									"ts-node-cwd": "dist/bin-cwd.js",
 | 
				
			||||||
 | 
									"ts-node-esm": "dist/bin-esm.js",
 | 
				
			||||||
 | 
									"ts-node-script": "dist/bin-script.js",
 | 
				
			||||||
 | 
									"ts-node-transpile-only": "dist/bin-transpile.js",
 | 
				
			||||||
 | 
									"ts-script": "dist/bin-script-deprecated.js"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"peerDependencies": {
 | 
				
			||||||
 | 
									"@swc/core": ">=1.2.50",
 | 
				
			||||||
 | 
									"@swc/wasm": ">=1.2.50",
 | 
				
			||||||
 | 
									"@types/node": "*",
 | 
				
			||||||
 | 
									"typescript": ">=2.7"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"peerDependenciesMeta": {
 | 
				
			||||||
 | 
									"@swc/core": {
 | 
				
			||||||
 | 
										"optional": true
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									"@swc/wasm": {
 | 
				
			||||||
 | 
										"optional": true
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/type-is": {
 | 
							"node_modules/type-is": {
 | 
				
			||||||
			"version": "1.6.18",
 | 
								"version": "1.6.18",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
 | 
								"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
 | 
				
			||||||
@@ -1864,6 +2135,19 @@
 | 
				
			|||||||
			"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
 | 
								"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
 | 
				
			||||||
			"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
 | 
								"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/typescript": {
 | 
				
			||||||
 | 
								"version": "5.6.2",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
 | 
				
			||||||
 | 
								"peer": true,
 | 
				
			||||||
 | 
								"bin": {
 | 
				
			||||||
 | 
									"tsc": "bin/tsc",
 | 
				
			||||||
 | 
									"tsserver": "bin/tsserver"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">=14.17"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/undefsafe": {
 | 
							"node_modules/undefsafe": {
 | 
				
			||||||
			"version": "2.0.5",
 | 
								"version": "2.0.5",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
 | 
								"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
 | 
				
			||||||
@@ -1872,8 +2156,7 @@
 | 
				
			|||||||
		"node_modules/undici-types": {
 | 
							"node_modules/undici-types": {
 | 
				
			||||||
			"version": "6.19.8",
 | 
								"version": "6.19.8",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
 | 
								"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
 | 
				
			||||||
			"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
 | 
								"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
 | 
				
			||||||
			"dev": true
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"node_modules/unpipe": {
 | 
							"node_modules/unpipe": {
 | 
				
			||||||
			"version": "1.0.0",
 | 
								"version": "1.0.0",
 | 
				
			||||||
@@ -1896,6 +2179,11 @@
 | 
				
			|||||||
				"node": ">= 0.4.0"
 | 
									"node": ">= 0.4.0"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/v8-compile-cache-lib": {
 | 
				
			||||||
 | 
								"version": "3.0.1",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"node_modules/vary": {
 | 
							"node_modules/vary": {
 | 
				
			||||||
			"version": "1.1.2",
 | 
								"version": "1.1.2",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
 | 
								"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
 | 
				
			||||||
@@ -1943,6 +2231,14 @@
 | 
				
			|||||||
			"version": "4.0.0",
 | 
								"version": "4.0.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
 | 
				
			||||||
			"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
 | 
								"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"node_modules/yn": {
 | 
				
			||||||
 | 
								"version": "3.1.1",
 | 
				
			||||||
 | 
								"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
 | 
				
			||||||
 | 
								"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
 | 
				
			||||||
 | 
								"engines": {
 | 
				
			||||||
 | 
									"node": ">=6"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,14 +13,18 @@
 | 
				
			|||||||
	"license": "ISC",
 | 
						"license": "ISC",
 | 
				
			||||||
	"keywords": [],
 | 
						"keywords": [],
 | 
				
			||||||
	"dependencies": {
 | 
						"dependencies": {
 | 
				
			||||||
 | 
							"axios": "^1.7.7",
 | 
				
			||||||
		"canvas": "^2.11.2",
 | 
							"canvas": "^2.11.2",
 | 
				
			||||||
		"dateformat": "^4.5.1",
 | 
							"dateformat": "^4.5.1",
 | 
				
			||||||
		"dayjs": "^1.11.7",
 | 
							"dayjs": "^1.11.7",
 | 
				
			||||||
		"dotenv": "^16.0.3",
 | 
							"dotenv": "^16.0.3",
 | 
				
			||||||
		"express": "^4.20.0",
 | 
							"express": "^4.20.0",
 | 
				
			||||||
 | 
							"form-data": "^4.0.0",
 | 
				
			||||||
		"fs": "^0.0.1-security",
 | 
							"fs": "^0.0.1-security",
 | 
				
			||||||
		"multer": "^1.4.5-lts.1",
 | 
							"multer": "^1.4.5-lts.1",
 | 
				
			||||||
		"nodemon": "^3.1.4"
 | 
							"mysql": "^2.18.1",
 | 
				
			||||||
 | 
							"nodemon": "^3.1.4",
 | 
				
			||||||
 | 
							"ts-node": "^10.9.2"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"devDependencies": {
 | 
						"devDependencies": {
 | 
				
			||||||
		"@types/dateformat": "^5.0.0",
 | 
							"@types/dateformat": "^5.0.0",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										116
									
								
								src/Engine/CCExtensions/ArrayExtension.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/Engine/CCExtensions/ArrayExtension.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					declare interface Array<T> {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 移除一個值並且回傳
 | 
				
			||||||
 | 
						 * @param index
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						ExRemoveAt(index: number): T;
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 移除全部值(注意. 參考的也會被清空)
 | 
				
			||||||
 | 
						 * @example
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 *  let bar: number[] = [1, 2, 3];
 | 
				
			||||||
 | 
						 *  let bar2: number[] = bar;
 | 
				
			||||||
 | 
						 *  bar.Clear();
 | 
				
			||||||
 | 
						 *  console.log(bar, bar2);
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 *  // {
 | 
				
			||||||
 | 
						 *  //     "bar": [],
 | 
				
			||||||
 | 
						 *  //     "bar2": []
 | 
				
			||||||
 | 
						 *  // }
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						Clear(): void;
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 物件陣列排序,asc&key陣列長度請一樣
 | 
				
			||||||
 | 
						 * PS. boolean 帶false是先true在false
 | 
				
			||||||
 | 
						 * @link JavaScript Object 排序 http://www.eion.com.tw/Blogger/?Pid=1170#:~:text=JavaScript%20Object%20排序
 | 
				
			||||||
 | 
						 * @param asc 是否升序排列(小到大)
 | 
				
			||||||
 | 
						 * @param key 需排序的key(優先順序左到右)(沒有就放空)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						ObjectSort(asc?: boolean[], key?: string[]): any[];
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 設計給Array<cc.Component.EventHandler>forHoldButton使用
 | 
				
			||||||
 | 
						 * Add a non persistent listener to the UnityEvent.
 | 
				
			||||||
 | 
						 * @param call Callback function.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						AddListener(call: Function): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Array.prototype.ExRemoveAt || Object.defineProperty(Array.prototype, "ExRemoveAt", {
 | 
				
			||||||
 | 
						enumerable: false,
 | 
				
			||||||
 | 
						value: function (index: number): any {
 | 
				
			||||||
 | 
							let item: any = this.splice(index, 1);
 | 
				
			||||||
 | 
							return item[0];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Array.prototype.Clear || Object.defineProperty(Array.prototype, "Clear", {
 | 
				
			||||||
 | 
						enumerable: false,
 | 
				
			||||||
 | 
						value: function (): void {
 | 
				
			||||||
 | 
							this.length = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// let foo: number[] = [1, 2, 3];
 | 
				
			||||||
 | 
							// let bar: number[] = [1, 2, 3];
 | 
				
			||||||
 | 
							// let foo2: number[] = foo;
 | 
				
			||||||
 | 
							// let bar2: number[] = bar;
 | 
				
			||||||
 | 
							// foo = [];
 | 
				
			||||||
 | 
							// bar.length = 0;
 | 
				
			||||||
 | 
							// console.log(foo, bar, foo2, bar2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// {
 | 
				
			||||||
 | 
							//     "foo": [],
 | 
				
			||||||
 | 
							//     "bar": [],
 | 
				
			||||||
 | 
							//     "foo2": [
 | 
				
			||||||
 | 
							//       1,
 | 
				
			||||||
 | 
							//       2,
 | 
				
			||||||
 | 
							//       3
 | 
				
			||||||
 | 
							//     ],
 | 
				
			||||||
 | 
							//     "bar2": []
 | 
				
			||||||
 | 
							// }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Array.prototype.ObjectSort || Object.defineProperty(Array.prototype, "ObjectSort", {
 | 
				
			||||||
 | 
						enumerable: false,
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param asc 是否升序排列(小到大)
 | 
				
			||||||
 | 
						 * @param key 需排序的key(優先順序左到右)(沒有就放空)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						value: function (asc: boolean[] = [true], key?: string[]): any[] {
 | 
				
			||||||
 | 
							if (this.length === 0) {
 | 
				
			||||||
 | 
								return this;
 | 
				
			||||||
 | 
							} else if (!key || key.length === 0) {
 | 
				
			||||||
 | 
								console.error(`ObjectSort key error`);
 | 
				
			||||||
 | 
								return this;
 | 
				
			||||||
 | 
							} else if (asc.length !== key.length) {
 | 
				
			||||||
 | 
								console.error(`ObjectSort key asc error asc.length: ${asc.length}, key.length: ${key.length}`);
 | 
				
			||||||
 | 
								return this;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for (let i: number = 0; i < key.length; i++) {
 | 
				
			||||||
 | 
								const keyname: string = key[i];
 | 
				
			||||||
 | 
								if (this[0][keyname] === undefined) {
 | 
				
			||||||
 | 
									console.error(`ObjectSort has not key[${i}]: ${keyname}`);
 | 
				
			||||||
 | 
									return this;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							let count: number = key ? key.length : 1;
 | 
				
			||||||
 | 
							let arr: any[];
 | 
				
			||||||
 | 
							for (let i: number = count - 1; i >= 0; i--) {
 | 
				
			||||||
 | 
								arr = this.sort(function (a: any, b: any): 1 | -1 {
 | 
				
			||||||
 | 
									let mya: any = a;
 | 
				
			||||||
 | 
									let myb: any = b;
 | 
				
			||||||
 | 
									if (key) {
 | 
				
			||||||
 | 
										mya = a[key[i]];
 | 
				
			||||||
 | 
										myb = b[key[i]];
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// 加個等於數字相同不要再去排序到
 | 
				
			||||||
 | 
									if (asc[i]) {
 | 
				
			||||||
 | 
										return mya >= myb ? 1 : -1;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										return mya <= myb ? 1 : -1;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return arr;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/Engine/CCExtensions/CCExtension.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/Engine/CCExtensions/CCExtension.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ver": "1.1.0",
 | 
				
			||||||
 | 
					  "uuid": "b373f805-9297-4af5-8ea6-0a250649b5b0",
 | 
				
			||||||
 | 
					  "importer": "typescript",
 | 
				
			||||||
 | 
					  "isPlugin": false,
 | 
				
			||||||
 | 
					  "loadPluginInWeb": true,
 | 
				
			||||||
 | 
					  "loadPluginInNative": true,
 | 
				
			||||||
 | 
					  "loadPluginInEditor": false,
 | 
				
			||||||
 | 
					  "subMetas": {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										189
									
								
								src/Engine/CCExtensions/NumberExtension.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/Engine/CCExtensions/NumberExtension.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,189 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					declare interface Number {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 金額每三位數(千)加逗號, 並且補到小數點第2位
 | 
				
			||||||
 | 
					     * 輸出 41,038,560.00  
 | 
				
			||||||
 | 
					     * @param precision 補到小數點第幾位
 | 
				
			||||||
 | 
					     * @param isPadZero 是否要補零
 | 
				
			||||||
 | 
					     * */
 | 
				
			||||||
 | 
					    ExFormatNumberWithComma(precision?: number, isPadZero?: boolean): string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 基本4位數(9,999-999B-T)
 | 
				
			||||||
 | 
					     * */
 | 
				
			||||||
 | 
					    ExTransferToBMK(precision?: number,offset?: number): string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 數字轉字串, 頭補0
 | 
				
			||||||
 | 
					     * @param size 
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Pad(size: number): string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 四捨五入到小數點第X位 (同server計算規則)
 | 
				
			||||||
 | 
					     * @param precision 
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ExToNumRoundDecimal(precision: number): number;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 無條件捨去到小數點第X位
 | 
				
			||||||
 | 
					     * @param precision 
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ExToNumFloorDecimal(precision: number): number;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 無條件捨去強制保留X位小數,如:2,會在2後面補上00.即2.00 
 | 
				
			||||||
 | 
					     * @param precision 補到小數點第幾位
 | 
				
			||||||
 | 
					     * @param isPadZero 是否要補零
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ExToStringFloorDecimal(precision: number, isPadZero?: boolean): string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 取整數)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ExToInt():number;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 小數轉整數(支援科學符號)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Float2Fixed():number;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 數字長度(支援科學符號)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    DigitLength():number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    target: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Number.prototype.ExFormatNumberWithComma || Object.defineProperty(Number.prototype, 'ExFormatNumberWithComma', {
 | 
				
			||||||
 | 
					    enumerable: false,
 | 
				
			||||||
 | 
					    value: function (precision: number = 2, isPadZero: boolean = true) {
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					        // let arr = String(this).split('.');
 | 
				
			||||||
 | 
					        let arr = this.ExToStringFloorDecimal(precision, isPadZero).split('.');
 | 
				
			||||||
 | 
					        let num = arr[0], result = '';
 | 
				
			||||||
 | 
					        while (num.length > 3) {
 | 
				
			||||||
 | 
					            result = ',' + num.slice(-3) + result;
 | 
				
			||||||
 | 
					            num = num.slice(0, num.length - 3);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (num.length > 0) result = num + result;
 | 
				
			||||||
 | 
					        return arr[1] ? result + '.' + arr[1] : result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Number.prototype.ExTransferToBMK || Object.defineProperty(Number.prototype, 'ExTransferToBMK', {
 | 
				
			||||||
 | 
					    enumerable: false,
 | 
				
			||||||
 | 
					    value: function (precision: number=2,offset: number = 0) {
 | 
				
			||||||
 | 
					        /**千 */
 | 
				
			||||||
 | 
					        let MONEY_1K: number = 1000;
 | 
				
			||||||
 | 
					        /**萬 */
 | 
				
			||||||
 | 
					        // let MONEY_10K: number = 10000;
 | 
				
			||||||
 | 
					        /**十萬 */
 | 
				
			||||||
 | 
					        // let MONEY_100K: number = 100000;
 | 
				
			||||||
 | 
					        /**百萬 */
 | 
				
			||||||
 | 
					        let MONEY_1M: number = 1000000;
 | 
				
			||||||
 | 
					        /**千萬 */
 | 
				
			||||||
 | 
					        // let MONEY_10M: number = 10000000;
 | 
				
			||||||
 | 
					        /**億 */
 | 
				
			||||||
 | 
					        // let MONEY_100M: number = 100000000;
 | 
				
			||||||
 | 
					        /**十億 */
 | 
				
			||||||
 | 
					        let MONEY_1B: number = 1000000000;
 | 
				
			||||||
 | 
					        /**百億 */
 | 
				
			||||||
 | 
					        // let MONEY_10B: number = 10000000000;
 | 
				
			||||||
 | 
					        /**千億 */
 | 
				
			||||||
 | 
					        // let MONEY_100B: number = 100000000000;
 | 
				
			||||||
 | 
					        /**兆 */
 | 
				
			||||||
 | 
					        // let MONEY_1T: number = 1000000000000;
 | 
				
			||||||
 | 
					        offset = Math.pow(10, offset);
 | 
				
			||||||
 | 
					        // if (this >= MONEY_1T * offset) {
 | 
				
			||||||
 | 
					        //     //(3)1,000T
 | 
				
			||||||
 | 
					        //     //1T~
 | 
				
			||||||
 | 
					        //     return (~~(this / MONEY_1T)).ExFormatNumberWithComma(0) + "T";
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        if (this >= MONEY_1B * offset) {
 | 
				
			||||||
 | 
					            //1,000B~900,000B
 | 
				
			||||||
 | 
					            //1B~900B
 | 
				
			||||||
 | 
					            return (this / MONEY_1B).ExFormatNumberWithComma(3, false) + "B";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (this >= MONEY_1M * offset) {
 | 
				
			||||||
 | 
					            //1,000M~900,000M
 | 
				
			||||||
 | 
					            //1M~900M
 | 
				
			||||||
 | 
					            return (this / MONEY_1M).ExFormatNumberWithComma(3, false) + "M";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (this >= MONEY_1K * offset) {
 | 
				
			||||||
 | 
					            //1,000K~900,000K
 | 
				
			||||||
 | 
					            //1K~90K
 | 
				
			||||||
 | 
					            return (this / MONEY_1K).ExFormatNumberWithComma(3, false) + "K";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            //0~9,000,000
 | 
				
			||||||
 | 
					            //0~9,000
 | 
				
			||||||
 | 
					            return this.ExFormatNumberWithComma(precision);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					Number.prototype.Pad || Object.defineProperty(Number.prototype, 'Pad', {
 | 
				
			||||||
 | 
					    enumerable: false,
 | 
				
			||||||
 | 
					    value: function (size: number) {
 | 
				
			||||||
 | 
					        let s = this + "";
 | 
				
			||||||
 | 
					        while (s.length < size) s = "0" + s;
 | 
				
			||||||
 | 
					        return s;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					Number.prototype.ExToNumRoundDecimal || Object.defineProperty(Number.prototype, 'ExToNumRoundDecimal', {
 | 
				
			||||||
 | 
					    enumerable: false,
 | 
				
			||||||
 | 
					    value: function (precision: number) {
 | 
				
			||||||
 | 
					        return Math.round(Math.round(this * Math.pow(10, (precision || 0) + 1)) / 10) / Math.pow(10, (precision || 0));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					Number.prototype.ExToInt || Object.defineProperty(Number.prototype, 'ExToInt',{
 | 
				
			||||||
 | 
					    enumerable: false,
 | 
				
			||||||
 | 
					    value: function (){
 | 
				
			||||||
 | 
					        return ~~this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					Number.prototype.ExToNumFloorDecimal || Object.defineProperty(Number.prototype, 'ExToNumFloorDecimal', {
 | 
				
			||||||
 | 
					    enumerable: false,
 | 
				
			||||||
 | 
					    value: function (precision: number) { 
 | 
				
			||||||
 | 
					        let str = this.toPrecision(12);
 | 
				
			||||||
 | 
					        let dotPos = str.indexOf('.');
 | 
				
			||||||
 | 
					        return dotPos == -1 ? this : +`${str.substr(0, dotPos + 1 + precision)}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					Number.prototype.ExToStringFloorDecimal || Object.defineProperty(Number.prototype, 'ExToStringFloorDecimal', {
 | 
				
			||||||
 | 
					    enumerable: false,
 | 
				
			||||||
 | 
					    value: function (precision: number, isPadZero: boolean = true) {
 | 
				
			||||||
 | 
					        // 取小數點第X位
 | 
				
			||||||
 | 
					        let f = this.ExToNumFloorDecimal(precision);
 | 
				
			||||||
 | 
					        let s = f.toString();
 | 
				
			||||||
 | 
					        // 補0
 | 
				
			||||||
 | 
					        if (isPadZero) {
 | 
				
			||||||
 | 
					            let rs = s.indexOf('.');
 | 
				
			||||||
 | 
					            if (rs < 0) {
 | 
				
			||||||
 | 
					                rs = s.length;
 | 
				
			||||||
 | 
					                s += '.';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            while (s.length <= rs + precision) {
 | 
				
			||||||
 | 
					                s += '0';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return s;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					Number.prototype.Float2Fixed || Object.defineProperty(Number.prototype, 'Float2Fixed', {
 | 
				
			||||||
 | 
					    enumerable: false,
 | 
				
			||||||
 | 
					    value: function () {
 | 
				
			||||||
 | 
					        if (this.toString().indexOf('e') === -1) {
 | 
				
			||||||
 | 
					        return Number(this.toString().replace('.', ''));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const dLen = this.DigitLength();
 | 
				
			||||||
 | 
					        return dLen > 0 ? +parseFloat((this * Math.pow(10, dLen)).toPrecision(12)) : this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					Number.prototype.DigitLength || Object.defineProperty(Number.prototype, 'DigitLength', {
 | 
				
			||||||
 | 
					    enumerable: false,
 | 
				
			||||||
 | 
					    value: function () {
 | 
				
			||||||
 | 
					        const eSplit = this.toString().split(/[eE]/);
 | 
				
			||||||
 | 
					        const len = (eSplit[0].split('.')[1] || '').length - (+(eSplit[1] || 0));
 | 
				
			||||||
 | 
					        return len > 0 ? len : 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
							
								
								
									
										84
									
								
								src/Engine/Number/NumberEx.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/Engine/Number/NumberEx.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					export module NumberEx {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 检测数字是否越界,如果越界给出提示
 | 
				
			||||||
 | 
						 * @param {*number} num 输入数
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						function checkBoundary(num: number) {
 | 
				
			||||||
 | 
							if (_boundaryCheckingState) {
 | 
				
			||||||
 | 
								if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
 | 
				
			||||||
 | 
									console.warn(`${num} is beyond boundary when transfer to integer, the results may not be accurate`);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 精确乘法
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						export function times(num1: number, num2: number, ...others: number[]): number {
 | 
				
			||||||
 | 
							if (others.length > 0) {
 | 
				
			||||||
 | 
								return times(times(num1, num2), others[0], ...others.slice(1));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							const num1Changed = num1.Float2Fixed();
 | 
				
			||||||
 | 
							const num2Changed = num2.Float2Fixed();
 | 
				
			||||||
 | 
							const baseNum = num1.DigitLength() + num2.DigitLength();
 | 
				
			||||||
 | 
							const leftValue = num1Changed * num2Changed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							checkBoundary(leftValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return leftValue / Math.pow(10, baseNum);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 精确加法
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						export function plus(num1: number, num2: number, ...others: number[]): number {
 | 
				
			||||||
 | 
							if (others.length > 0) {
 | 
				
			||||||
 | 
								return plus(plus(num1, num2), others[0], ...others.slice(1));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
 | 
				
			||||||
 | 
							return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 精确减法
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						export function minus(num1: number, num2: number, ...others: number[]): number {
 | 
				
			||||||
 | 
							if (others.length > 0) {
 | 
				
			||||||
 | 
								return minus(minus(num1, num2), others[0], ...others.slice(1));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
 | 
				
			||||||
 | 
							return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 精确除法
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						export function divide(num1: number, num2: number, ...others: number[]): number {
 | 
				
			||||||
 | 
							if (others.length > 0) {
 | 
				
			||||||
 | 
								return divide(divide(num1, num2), others[0], ...others.slice(1));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							const num1Changed = num1.Float2Fixed();
 | 
				
			||||||
 | 
							const num2Changed = num2.Float2Fixed();
 | 
				
			||||||
 | 
							checkBoundary(num1Changed);
 | 
				
			||||||
 | 
							checkBoundary(num2Changed);
 | 
				
			||||||
 | 
							return times((num1Changed / num2Changed), Math.pow(10, num2.DigitLength() - num1.DigitLength()));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 四舍五入
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						export function round(num: number, ratio: number): number {
 | 
				
			||||||
 | 
							const base = Math.pow(10, ratio);
 | 
				
			||||||
 | 
							return divide(Math.round(times(num, base)), base);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let _boundaryCheckingState = false;
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 是否进行边界检查
 | 
				
			||||||
 | 
						 * @param flag 标记开关,true 为开启,false 为关闭
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						function enableBoundaryChecking(flag = true) {
 | 
				
			||||||
 | 
							_boundaryCheckingState = flag;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										90
									
								
								src/Engine/Number/RandomEx.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/Engine/Number/RandomEx.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					export module RandomEx {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 取得隨機布林值
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    export function GetBool() {
 | 
				
			||||||
 | 
					        return GetInt() >= 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 取得隨機整數(回傳min ~ max - 1)
 | 
				
			||||||
 | 
					     * @param min
 | 
				
			||||||
 | 
					     * @param max
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    export function GetInt(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number {
 | 
				
			||||||
 | 
					        return Math.floor(Math.random() * (max - min)) + min;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 取得隨機小數
 | 
				
			||||||
 | 
					     * @param min 
 | 
				
			||||||
 | 
					     * @param max 
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    export function GetFloat(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number {
 | 
				
			||||||
 | 
					        return Math.random() * (max - min) + min;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 隨機取得複數個不重複回傳
 | 
				
			||||||
 | 
					     * @param num 取得數量
 | 
				
			||||||
 | 
					     * @param items 陣列 
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    export function GetMultiNoRepeat(num: number, items: any[]): any[] {
 | 
				
			||||||
 | 
					        let result: any[] = [];
 | 
				
			||||||
 | 
					        for (let i: number = 0; i < num; i++) {
 | 
				
			||||||
 | 
					            let ran: number = Math.floor(Math.random() * items.length);
 | 
				
			||||||
 | 
					            let item = items.splice(ran, 1)[0];
 | 
				
			||||||
 | 
					            if (result.indexOf(item) == -1) {
 | 
				
			||||||
 | 
					                result.push(item);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根據權重取得複數個不重複回傳
 | 
				
			||||||
 | 
					     * @param prize 獎項
 | 
				
			||||||
 | 
					     * @param weights 機率
 | 
				
			||||||
 | 
					     * @param count 數量
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    export function GetMultiNoRepeatByWeight(prize: any[], weights: number[] = null, count: number = 1): any[] {
 | 
				
			||||||
 | 
					        if (weights === null) {
 | 
				
			||||||
 | 
					            weights = [];
 | 
				
			||||||
 | 
					            for (let i: number = 0; i < prize.length; i++) {
 | 
				
			||||||
 | 
					                weights.push(1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let target: any[] = [];
 | 
				
			||||||
 | 
					        for (let i: number = 0; i < count; i++) {
 | 
				
			||||||
 | 
					            let results: number[] = RandomEx.GetPrizeByWeight(prize, weights);
 | 
				
			||||||
 | 
					            prize.splice(results[0], 1);
 | 
				
			||||||
 | 
					            weights.splice(results[0], 1);
 | 
				
			||||||
 | 
					            target.push(results[1]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return target;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根據權重隨機取值
 | 
				
			||||||
 | 
					     * @param prize 獎項
 | 
				
			||||||
 | 
					     * @param weights 機率
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    export function GetPrizeByWeight(prize: any[], weights: number[]): any[] {
 | 
				
			||||||
 | 
					        if (prize.length !== weights.length) {
 | 
				
			||||||
 | 
					            console.error(`GetWeight error -> prize.length:${prize.length} !== weights.length:${weights.length}`);
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let totalWeight: number = 0;
 | 
				
			||||||
 | 
					        for (let i: number = 0; i < weights.length; i++) {
 | 
				
			||||||
 | 
					            totalWeight += weights[i];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let random: number = RandomEx.GetInt(0, totalWeight) + 1;
 | 
				
			||||||
 | 
					        let nowWeight: number = weights[0];
 | 
				
			||||||
 | 
					        for (let i: number = 0; i < weights.length; i++) {
 | 
				
			||||||
 | 
					            if (nowWeight >= random) {
 | 
				
			||||||
 | 
					                return [i, prize[i]];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            nowWeight += weights[i + 1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/Engine/String.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/Engine/String.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					interface StringConstructor {
 | 
				
			||||||
 | 
						IsNullOrEmpty: (value: string) => boolean;
 | 
				
			||||||
 | 
						Format: (format: string, ...args: any[]) => string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String.IsNullOrEmpty = function (value: string): boolean {
 | 
				
			||||||
 | 
						return value === undefined || value === null || value.trim() === "";
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String.Format = function (format: string, ...args: any[]): string {
 | 
				
			||||||
 | 
						return format.replace(/{(\d+)}/g, (match, index) => {
 | 
				
			||||||
 | 
							let value: any = args[index];
 | 
				
			||||||
 | 
							if (value === null || value === undefined) { return ""; }
 | 
				
			||||||
 | 
							return "" + value;
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										13
									
								
								src/Tools.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/Tools.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Tools
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default class Tools {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//#region Custom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static Sleep(ms: number): Promise<any> {
 | 
				
			||||||
 | 
							return new Promise(resolve => setTimeout(resolve, ms));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//#endregion
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/app.ts
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/app.ts
									
									
									
									
									
								
							@@ -6,12 +6,15 @@
 | 
				
			|||||||
import dayjs from "dayjs";
 | 
					import dayjs from "dayjs";
 | 
				
			||||||
import "dayjs/locale/zh-tw";
 | 
					import "dayjs/locale/zh-tw";
 | 
				
			||||||
import dotenv from "dotenv";
 | 
					import dotenv from "dotenv";
 | 
				
			||||||
 | 
					import "./Engine/CCExtensions/ArrayExtension";
 | 
				
			||||||
 | 
					import "./Engine/CCExtensions/NumberExtension";
 | 
				
			||||||
 | 
					import "./Engine/String";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import bodyParser from "body-parser";
 | 
				
			||||||
import dateFormat from "dateformat";
 | 
					import dateFormat from "dateformat";
 | 
				
			||||||
import express from 'express';
 | 
					import express from "express";
 | 
				
			||||||
import fs from "fs";
 | 
					import fs from "fs";
 | 
				
			||||||
import https from 'https';
 | 
					import https from "https";
 | 
				
			||||||
import multer from 'multer';
 | 
					 | 
				
			||||||
import { generateImage } from "./canvas";
 | 
					import { generateImage } from "./canvas";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dayjs.locale("zh-tw");
 | 
					dayjs.locale("zh-tw");
 | 
				
			||||||
@@ -24,7 +27,6 @@ if (process.env.NODE_ENV) {
 | 
				
			|||||||
const path: string = process.env.URLPATH || "/";
 | 
					const path: string = process.env.URLPATH || "/";
 | 
				
			||||||
const port: number = +process.env.PORT || 3000;
 | 
					const port: number = +process.env.PORT || 3000;
 | 
				
			||||||
const app = express();
 | 
					const app = express();
 | 
				
			||||||
const upload = multer({ dest: `${path}/` }); // 用于处理文件上传
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
//讀取憑證及金鑰
 | 
					//讀取憑證及金鑰
 | 
				
			||||||
const prikey: string = fs.readFileSync(process.env.prikeyPath, "utf8");
 | 
					const prikey: string = fs.readFileSync(process.env.prikeyPath, "utf8");
 | 
				
			||||||
@@ -38,11 +40,18 @@ const credentials: Object = {
 | 
				
			|||||||
	ca: cafile
 | 
						ca: cafile
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 处理 POST 请求并返回生成的图片
 | 
					app.use(bodyParser.json());
 | 
				
			||||||
app.post(`/${path}`, upload.single('file'), (req, res) => {
 | 
					// 设置 POST 路由来返回图片
 | 
				
			||||||
	const imageBuffer = generateImage();
 | 
					app.post(`/${path}`, (req, res) => {
 | 
				
			||||||
	res.set('Content-Type', 'image/png');
 | 
						try {
 | 
				
			||||||
	res.send(imageBuffer);
 | 
							const data = req.body;
 | 
				
			||||||
 | 
							const imageBuffer = generateImage(data);
 | 
				
			||||||
 | 
							res.setHeader("Content-Type", "image/png");
 | 
				
			||||||
 | 
							res.send(imageBuffer);
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.error("Error generating image:", error);
 | 
				
			||||||
 | 
							res.status(500).send("Error generating image");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 创建 HTTPS 服务器
 | 
					// 创建 HTTPS 服务器
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										131
									
								
								src/canvas.ts
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								src/canvas.ts
									
									
									
									
									
								
							@@ -1,24 +1,36 @@
 | 
				
			|||||||
import { createCanvas } from 'canvas';
 | 
					import { createCanvas, registerFont } from "canvas";
 | 
				
			||||||
 | 
					import dateFormat from "dateformat";
 | 
				
			||||||
 | 
					import { lineNotify } from "./lineNotify";
 | 
				
			||||||
 | 
					import { sqlSendQuery } from "./sql";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 生成图片的函数
 | 
					// 生成图片的函数
 | 
				
			||||||
export function generateImage() {
 | 
					export function generateImage(props: ICanvas) {
 | 
				
			||||||
	const boys = ['男生A', '男生B', '男生C'];
 | 
						const { date, team, rounds = 3 } = props;
 | 
				
			||||||
	const girls = ['女生A', '女生B', '女生C'];
 | 
						const formatDate = `${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6, 8)}`;
 | 
				
			||||||
	const rounds = 3; // 可以根据需要设置轮数
 | 
						const { aTeam, bTeam } = splitTeams(team);
 | 
				
			||||||
	const person = boys.length > girls.length ? boys.length : girls.length;
 | 
					
 | 
				
			||||||
	if (boys.length > girls.length) {
 | 
						if (!aTeam || !bTeam) {
 | 
				
			||||||
		girls.push("那個")
 | 
							return "";
 | 
				
			||||||
	} else if (boys.length < girls.length) {
 | 
						}
 | 
				
			||||||
		boys.push("那個")
 | 
						const person = aTeam.length > bTeam.length ? aTeam.length : bTeam.length;
 | 
				
			||||||
 | 
						if (aTeam.length > bTeam.length) {
 | 
				
			||||||
 | 
							bTeam.push("那個")
 | 
				
			||||||
 | 
						} else if (aTeam.length < bTeam.length) {
 | 
				
			||||||
 | 
							aTeam.push("那個")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	const canvasWidth = 800; // 根据需要调整宽度
 | 
						const canvasWidth = 800; // 根据需要调整宽度
 | 
				
			||||||
	const canvasHeight = (rounds * 100) + (person * 10); // 根据需要调整高度
 | 
						const canvasHeight = (rounds * 100) + (person * 10); // 根据需要调整高度
 | 
				
			||||||
	const canvas = createCanvas(canvasWidth, canvasHeight);
 | 
						const canvas = createCanvas(canvasWidth, canvasHeight);
 | 
				
			||||||
	const ctx = canvas.getContext('2d');
 | 
						const ctx = canvas.getContext("2d");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 注册自定义字体
 | 
				
			||||||
 | 
						const fontPath = "./src/fonts/PMingLiU.ttf";
 | 
				
			||||||
 | 
						registerFont(fontPath, { family: "PMingLiU" });
 | 
				
			||||||
	const font = "PMingLiU";
 | 
						const font = "PMingLiU";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 设定背景颜色
 | 
						// 设定背景颜色
 | 
				
			||||||
	ctx.fillStyle = 'white';
 | 
						ctx.fillStyle = "white";
 | 
				
			||||||
	ctx.fillRect(0, 0, canvas.width, canvas.height);
 | 
						ctx.fillRect(0, 0, canvas.width, canvas.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 追踪已使用的组合
 | 
						// 追踪已使用的组合
 | 
				
			||||||
@@ -27,11 +39,11 @@ export function generateImage() {
 | 
				
			|||||||
	// 生成不重复的队伍组合并确保每人上场
 | 
						// 生成不重复的队伍组合并确保每人上场
 | 
				
			||||||
	function generateTeams(): { teamA: string, teamB: string }[] {
 | 
						function generateTeams(): { teamA: string, teamB: string }[] {
 | 
				
			||||||
		const roundTeams: { teamA: string, teamB: string }[] = [];
 | 
							const roundTeams: { teamA: string, teamB: string }[] = [];
 | 
				
			||||||
		const availableBoys = [...boys];
 | 
							const availableBoys = [...aTeam];
 | 
				
			||||||
		const availableGirls = [...girls];
 | 
							const availableGirls = [...bTeam];
 | 
				
			||||||
		let attempts = 0;
 | 
							let attempts = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		while (roundTeams.length < boys.length) {
 | 
							while (roundTeams.length < aTeam.length) {
 | 
				
			||||||
			const boyIndex = Math.floor(Math.random() * availableBoys.length);
 | 
								const boyIndex = Math.floor(Math.random() * availableBoys.length);
 | 
				
			||||||
			const girlIndex = Math.floor(Math.random() * availableGirls.length);
 | 
								const girlIndex = Math.floor(Math.random() * availableGirls.length);
 | 
				
			||||||
			const team = `${availableBoys[boyIndex]}-${availableGirls[girlIndex]}`;
 | 
								const team = `${availableBoys[boyIndex]}-${availableGirls[girlIndex]}`;
 | 
				
			||||||
@@ -49,7 +61,7 @@ export function generateImage() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			// 如果尝试过多次未找到合适组合,强制生成剩余队伍
 | 
								// 如果尝试过多次未找到合适组合,强制生成剩余队伍
 | 
				
			||||||
			attempts++;
 | 
								attempts++;
 | 
				
			||||||
			if (attempts > 10 && roundTeams.length < boys.length) {
 | 
								if (attempts > 10 && roundTeams.length < aTeam.length) {
 | 
				
			||||||
				for (let i = 0; i < availableBoys.length; i++) {
 | 
									for (let i = 0; i < availableBoys.length; i++) {
 | 
				
			||||||
					roundTeams.push({
 | 
										roundTeams.push({
 | 
				
			||||||
						teamA: availableBoys[i],
 | 
											teamA: availableBoys[i],
 | 
				
			||||||
@@ -70,43 +82,43 @@ export function generateImage() {
 | 
				
			|||||||
		teamCombinations.push(newRound);
 | 
							teamCombinations.push(newRound);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 如果所有组合都用过了,重置组合记录
 | 
							// 如果所有组合都用过了,重置组合记录
 | 
				
			||||||
		if (usedCombinations.size >= boys.length * girls.length) {
 | 
							if (usedCombinations.size >= aTeam.length * bTeam.length) {
 | 
				
			||||||
			usedCombinations.clear();
 | 
								usedCombinations.clear();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 绘制标题和日期
 | 
						// 绘制标题和日期
 | 
				
			||||||
	ctx.fillStyle = 'black';
 | 
						ctx.fillStyle = "black";
 | 
				
			||||||
	ctx.font = `bold 24px "${font}"`;
 | 
						ctx.font = `bold 24px "${font}"`;
 | 
				
			||||||
	ctx.textAlign = 'center';
 | 
						ctx.textAlign = "center";
 | 
				
			||||||
	ctx.fillText('比賽隊伍', canvasWidth / 2, 40);
 | 
						ctx.fillText(`${formatDate} 比賽隊伍`, canvasWidth / 2, 40);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.font = `italic 16px "${font}"`;
 | 
						const roundTitleYStart = 80;
 | 
				
			||||||
	ctx.fillText('2024-09-09', canvasWidth / 2, 70);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const roundTitleYStart = 120;
 | 
					 | 
				
			||||||
	const teamYStart = 60;
 | 
						const teamYStart = 60;
 | 
				
			||||||
	const lineHeight = 30;
 | 
						const lineHeight = 30;
 | 
				
			||||||
	const roundsPerRow = 3;
 | 
						const roundsPerRow = 3;
 | 
				
			||||||
	const rowHeight = 300;
 | 
						const rowHeight = 300;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const result = {};
 | 
				
			||||||
	teamCombinations.forEach((roundTeams, roundIndex) => {
 | 
						teamCombinations.forEach((roundTeams, roundIndex) => {
 | 
				
			||||||
 | 
							result[roundIndex] = []
 | 
				
			||||||
		const rowIndex = Math.floor(roundIndex / roundsPerRow);
 | 
							const rowIndex = Math.floor(roundIndex / roundsPerRow);
 | 
				
			||||||
		const colIndex = roundIndex % roundsPerRow;
 | 
							const colIndex = roundIndex % roundsPerRow;
 | 
				
			||||||
		const roundXPosition = (canvasWidth / roundsPerRow) * colIndex + (canvasWidth / roundsPerRow) / 2;
 | 
							const roundXPosition = (canvasWidth / roundsPerRow) * colIndex + (canvasWidth / roundsPerRow) / 2;
 | 
				
			||||||
		const roundYPosition = roundTitleYStart + rowIndex * rowHeight;
 | 
							const roundYPosition = roundTitleYStart + rowIndex * rowHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.font = `bold 18px "${font}"`;
 | 
							ctx.font = `bold 18px "${font}"`;
 | 
				
			||||||
		ctx.textAlign = 'center';
 | 
							ctx.textAlign = "center";
 | 
				
			||||||
		ctx.fillText(`第 ${roundIndex + 1} 輪`, roundXPosition, roundYPosition);
 | 
							ctx.fillText(`第 ${roundIndex + 1} 輪`, roundXPosition, roundYPosition);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.font = `16px "${font}"`;
 | 
							ctx.font = `16px "${font}"`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		roundTeams.forEach((team, teamIndex) => {
 | 
							roundTeams.forEach((team, teamIndex) => {
 | 
				
			||||||
 | 
								result[roundIndex].push([team.teamA, team.teamB])
 | 
				
			||||||
			const teamYPosition = roundYPosition + teamYStart + teamIndex * lineHeight;
 | 
								const teamYPosition = roundYPosition + teamYStart + teamIndex * lineHeight;
 | 
				
			||||||
			if (teamIndex === 0) {
 | 
								if (teamIndex === 0) {
 | 
				
			||||||
				ctx.fillText('1號隊友', roundXPosition - 50, teamYPosition);
 | 
									ctx.fillText("1號隊友", roundXPosition - 50, teamYPosition);
 | 
				
			||||||
				ctx.fillText('2號隊友', roundXPosition + 50, teamYPosition);
 | 
									ctx.fillText("2號隊友", roundXPosition + 50, teamYPosition);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.fillText(team.teamA, roundXPosition - 50, teamYPosition + lineHeight);
 | 
								ctx.fillText(team.teamA, roundXPosition - 50, teamYPosition + lineHeight);
 | 
				
			||||||
			ctx.fillText(team.teamB, roundXPosition + 50, teamYPosition + lineHeight);
 | 
								ctx.fillText(team.teamB, roundXPosition + 50, teamYPosition + lineHeight);
 | 
				
			||||||
@@ -114,8 +126,67 @@ export function generateImage() {
 | 
				
			|||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 导出图片
 | 
						// 导出图片
 | 
				
			||||||
	const buffer = canvas.toBuffer('image/png');
 | 
						const buffer = canvas.toBuffer("image/png");
 | 
				
			||||||
	// fs.writeFileSync('./team_combination.png', buffer);
 | 
						// fs.writeFileSync("./team_combination.png", buffer);
 | 
				
			||||||
	console.log('圖片已生成!');
 | 
						let dateTime: string = dateFormat(new Date(), "yyyy-mm-dd HH:MM:ss");
 | 
				
			||||||
 | 
						console.log(`${dateTime} [canvas] 圖片已生成!`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const personnelStr: string = JSON.stringify(team);
 | 
				
			||||||
 | 
						const resultStr: string = JSON.stringify(result);
 | 
				
			||||||
 | 
						const query: string = String.Format("INSERT INTO `badminton` (time,personnel,battlecombination) VALUES ({0},'{1}','{2}') ON DUPLICATE KEY UPDATE personnel='{1}',battlecombination='{2}';"
 | 
				
			||||||
 | 
							, date, personnelStr, resultStr);
 | 
				
			||||||
 | 
						sqlSendQuery(query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const message = `\n${formatDate} 比賽隊伍`;
 | 
				
			||||||
 | 
						lineNotify({
 | 
				
			||||||
 | 
							LINE_ACCESS_TOKEN: process.env.LINE_ACCESS_TOKEN,
 | 
				
			||||||
 | 
							message,
 | 
				
			||||||
 | 
							imageFile: buffer
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	return buffer;
 | 
						return buffer;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ICanvas {
 | 
				
			||||||
 | 
						date: string;
 | 
				
			||||||
 | 
						team: any;
 | 
				
			||||||
 | 
						rounds: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function splitTeams(data: [number, string][]): { aTeam: string[], bTeam: string[] } {
 | 
				
			||||||
 | 
						const aTeam: string[] = [];
 | 
				
			||||||
 | 
						const bTeam: string[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 分离 boys 和 girls
 | 
				
			||||||
 | 
						data.forEach(([type, name]) => {
 | 
				
			||||||
 | 
							if (type === 1) {
 | 
				
			||||||
 | 
								aTeam.push(name);
 | 
				
			||||||
 | 
							} else if (type === 0) {
 | 
				
			||||||
 | 
								bTeam.push(name);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 进行平衡
 | 
				
			||||||
 | 
						while (aTeam.length > bTeam.length + 1) {
 | 
				
			||||||
 | 
							bTeam.push(aTeam.pop()!); // 移动一名男生到女生
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 平衡后可能还有空缺
 | 
				
			||||||
 | 
						const missingBoys = aTeam.length;
 | 
				
			||||||
 | 
						const missingGirls = bTeam.length;
 | 
				
			||||||
 | 
						const balanceSize = Math.max(missingBoys, missingGirls);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (missingBoys > balanceSize) {
 | 
				
			||||||
 | 
							// 若男生过多,添加补充人员到女生
 | 
				
			||||||
 | 
							for (let i = 0; i < missingBoys; i++) {
 | 
				
			||||||
 | 
								bTeam.push("那個");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (missingGirls > balanceSize) {
 | 
				
			||||||
 | 
							// 若女生过多,添加补充人员到男生
 | 
				
			||||||
 | 
							for (let i = 0; i < missingGirls; i++) {
 | 
				
			||||||
 | 
								aTeam.push("那個");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return { aTeam, bTeam };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								src/fonts/PMingLiU.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/fonts/PMingLiU.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										35
									
								
								src/lineNotify.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/lineNotify.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					import FormData from "form-data";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// lineNotify
 | 
				
			||||||
 | 
					export function lineNotify(props: ILineNotify) {
 | 
				
			||||||
 | 
						const { LINE_ACCESS_TOKEN, message, imageFile } = props;
 | 
				
			||||||
 | 
						let data = new FormData();
 | 
				
			||||||
 | 
						data.append("message", message);
 | 
				
			||||||
 | 
						if (imageFile) {
 | 
				
			||||||
 | 
							// 添加圖像文件
 | 
				
			||||||
 | 
							data.append("imageFile", imageFile, "image.jpg");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let config = {
 | 
				
			||||||
 | 
							method: "post",
 | 
				
			||||||
 | 
							maxBodyLength: Infinity,
 | 
				
			||||||
 | 
							url: "https://notify-api.line.me/api/notify",
 | 
				
			||||||
 | 
							headers: {
 | 
				
			||||||
 | 
								"Authorization": `Bearer ${LINE_ACCESS_TOKEN}`,
 | 
				
			||||||
 | 
								...data.getHeaders()
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							data: data
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						axios.request(config)
 | 
				
			||||||
 | 
							.then((response) => {
 | 
				
			||||||
 | 
								// console.log(JSON.stringify(response.data));
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							.catch((error) => {
 | 
				
			||||||
 | 
								console.log(error);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ILineNotify { LINE_ACCESS_TOKEN: string, message: string, imageFile?: Buffer }
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/sql.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/sql.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import mysql from "mysql";
 | 
				
			||||||
 | 
					import Tools from "./Tools";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function sqlSendQuery(query: string) {
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const connection: mysql.Connection = mysql.createConnection({
 | 
				
			||||||
 | 
								host: process.env.DB_HOST,
 | 
				
			||||||
 | 
								port: +process.env.DB_PORT,
 | 
				
			||||||
 | 
								user: process.env.DB_USER,
 | 
				
			||||||
 | 
								password: process.env.DB_PASSWORD,
 | 
				
			||||||
 | 
								database: process.env.DB_DATABASE
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							connection.connect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let resp: any = null;
 | 
				
			||||||
 | 
							let run: boolean = true;
 | 
				
			||||||
 | 
							connection.query(query, function (err: mysql.MysqlError, rows: any, fields: mysql.FieldInfo[]): void {
 | 
				
			||||||
 | 
								if (err) {
 | 
				
			||||||
 | 
									console.error(`${query} Error: \n${err.message}`);
 | 
				
			||||||
 | 
									run = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								resp = rows;
 | 
				
			||||||
 | 
								run = false;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							while (run) {
 | 
				
			||||||
 | 
								await Tools.Sleep(100);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// 释放连接
 | 
				
			||||||
 | 
							connection.end();
 | 
				
			||||||
 | 
							return resp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.error("MySQL 连接失败:", error);
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user