This commit is contained in:
2024-08-10 20:35:02 +08:00
parent 88233c58e3
commit 90f6ed0bc3
50 changed files with 5333 additions and 0 deletions

24
Server/plugs/Logger.ts Normal file
View File

@@ -0,0 +1,24 @@
class _Logger{
constructor(){
}
formatTime(): string{
let date = new Date();
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
}
info(msg: string){
console.log(`[${this.formatTime()}][INFO] ${msg}`);
}
warn(msg: string){
console.log(`[${this.formatTime()}][WARN] ${msg}`);
}
error(msg: string){
console.log(`[${this.formatTime()}][ERROR] ${msg}`);
}
}
let Logger = new _Logger();
export default Logger;

View File

@@ -0,0 +1,54 @@
// 读取上级目录的Settingconfig.json文件
import * as fs from 'fs';
import * as path from 'path';
import Logger from './Logger';
interface MySqlConfig {
host: string;
user: string;
password: string;
database: string;
}
interface ServiceRotationVerificationType{
AllowMaxTryCount: number;
AllowMaxAngleDiff: number;
ExpriedTimeSec: number;
}
interface ServiceType {
rotationVerification: ServiceRotationVerificationType
}
interface SettingConfigType {
mysql: MySqlConfig;
service: ServiceType;
}
const SettingConfig: SettingConfigType = {
mysql:{
host: "server.tonesc.cn",
user: "root",
password: "245565",
database: "tonecn"
},
service: {
rotationVerification: {
AllowMaxTryCount: 5,// 最大允许尝试次数
AllowMaxAngleDiff: 10,// 允许的最大角度差异
ExpriedTimeSec: 60// 单次session过期时间,单位秒
}
}
};
// (async () => {
// const filePath = path.join(__dirname, '..', 'Settingconfig.json');
// let readRes = fs.readFileSync(filePath, 'utf-8');
// try {
// let config = JSON.parse(readRes);
// Object.assign(SettingConfig, config);
// } catch (error) {
// Logger.error(`配置文件出错 ${error}`)
// throw new Error('配置文件格式错误');
// }
// })()
export default SettingConfig

View File

@@ -0,0 +1,69 @@
// MYSQL数据库连接池
// 版本v0.1
const mysql = require('mysql2/promise');
import Logger from "../Logger";
import SettingConfig from "../Settingconfig";
class MySQLConnectPool{
pool: any;
constructor()
{
this.pool = this.createConnectPool();
Logger.info("[MySQL] 数据库连接池已创建")
setTimeout(async () => {
let res = await this.testConnection();
if(res)
Logger.info("[MySQL] 数据库测试成功")
else
Logger.error("[MySQL] 数据库测试失败")
}, 10);
}
// 内部函数,无需手动调用
createConnectPool(){
return mysql.createPool({
host: SettingConfig.mysql.host,
database: SettingConfig.mysql.database,
user: SettingConfig.mysql.user,
password: SettingConfig.mysql.password,
waitForConnections:true,
connectionLimit:10,
queueLimit:0
})
}
// 内部函数,无需手动调用
async testConnection(){
try {
let res = await this.execute("SELECT 1 + 1 As result");
if(res[0].result == 2)
return 1;
else
return 0;
} catch (error) {
Logger.error(`[MYSQL] 数据库测试发生了错误:`+error);
return 0;
}
}
// 执行SQL语句
async execute(sql: string,values = undefined){
let connection;
try {
connection = await this.pool.getConnection();
let [rows, fields] = await connection.execute(sql,values);
return rows;
} catch (error) {
Logger.error("[MYSQL] 数据库发生错误:"+error);
return undefined;
} finally{
if(connection)
connection.release();
}
}
}
let MySQLConnection = new MySQLConnectPool();
export default MySQLConnection;

View File

@@ -0,0 +1,98 @@
const Redis = require('ioredis');
import Logger from "../Logger";
class _RedisConnection{
pool: any;
constructor(){
this.pool = new Redis({
host: 'server.tonesc.cn',
password: '2Pj4Ss9al3mS1',
connectionPoolSize: 10,
});
Logger.info('[Redis] 数据库连接池已创建')
setTimeout(async () => {
let res = await this.set('redis_test', '1');
if(res)
Logger.info('[Redis] 数据库测试成功')
else
Logger.error('[Redis] 数据库测试失败')
}, 10);
}
/**
* 设置键值对
* @param {*} key
* @param {*} value
* @returns { Promise<Boolean> } 成功返回true失败返回false
*/
set(key: string, value: string){
return new Promise((resolve) => {
this.pool.set(key, value, (err: any, value: any) => {
if (err) {
Logger.error('[Redis] 设置键时发生错误:' + err);
resolve(false);
} else {
resolve(true);
}
})
})
}
/**
* 获取键值对成功返回值失败返回undefined
* @param {string} key
* @returns { Promise<String> } 成功返回值失败返回undefined
*/
get(key: string): Promise<string> {
return new Promise((resolve) => {
this.pool.get(key, (err: any, value: any) => {
if (err) {
Logger.error('[Redis] 获取键时发生错误:' + err);
throw new Error('Redis连接错误')
} else {
resolve(value);
}
})
})
}
/**
* 删除键值对成功返回true失败返回false
* @param {string} key
* @returns { Promise<Boolean> } 成功返回true失败返回false
*/
del(key: string){
return new Promise((resolve) => {
this.pool.del(key, (err: any, value: any) => {
if (err) {
Logger.error('[Redis] 删除键时发生错误:' + err);
resolve(false);
} else {
resolve(true);
}
})
})
}
/**
* 设置键的过期时间
* @param {*} key
* @param {*} seconds
* @returns { Promise<Boolean> } 成功返回true失败返回false
*/
expire(key: any, seconds: number){
return new Promise((resolve) => {
this.pool.expire(key, seconds, (err: number, value: number) => {
if (err) {
Logger.error('[Redis] 设置键过期时发生错误:' + err);
resolve(false);
} else {
resolve(true);
}
})
})
}
}
const RedisConnection = new _RedisConnection();
export default RedisConnection;

View File

@@ -0,0 +1,100 @@
import Logger from "@plugs/Logger";
import SettingConfig from "@plugs/Settingconfig";
import RedisConnection from "@plugs/database/RedisConnection";
class RotationVerificationService{
constructor(){
Logger.info('[Service][RotationVerificationService] 旋转图像验证服务已启动')
}
async #get(session: string){
// 通过session获取单个存储对象无需手动调用
try {
let result: = await RedisConnection.get('RotationVerificationService:' + session);
return JSON.parse(result);// 由于RVService的值为JSON字符串因此需要解析
} catch (error) {
// 不一定是数据库出错,有可能是已过期
Logger.warn(`[Service][RotationVerificationService] 获取session[${session}]时发生错误:${error}`);
return undefined;
}
}
async #remove(session: string){
// 删除session无需手动调用
let res = await RedisConnection.del('RotationVerificationService:' + session);
if(!res)
Logger.err(`[Service][RotationVerificationService] 删除session[${session}]失败`);
}
/**
* 将session和角度信息存储便于后续验证
* @param {*} session
* @param {*} rotateDeg
*/
async add(session: string,rotateDeg: number){
let result = {
rotateDeg: rotateDeg,
tryCount: 0,
isPassed: false
}
let res = await RedisConnection.set('RotationVerificationService:' + session, JSON.stringify(result));
if(!res)
{
Logger.err(`[Service][RotationVerificationService] 存储session[${session}]失败`);
return false;
}
// 设置过期时间
RedisConnection.expire('RotationVerificationService:' + session, this.ExpriedTimeSec);
return true;
}
/**
* 查询该session是否已通过验证使用后自动实效
* @param {*} session
* @returns {Boolean} 验证通过结果
*/
async isPassed(session){
// 通过后有效期60s
let result = await this.#get(session);
if(!result)
return false;
let res = result.isPassed;
if(res)
// 验证通过该session失效防止重复验证
this.#remove(session);
return res;
}
/**
* 检查session和角度信息
* @param {*} session
* @param {*} rotateDeg
* @returns {Number} 0,已过期(或未加入验证池) -1,超过最大尝试次数 1,验证通过 -2,角度差异过大
*/
async check(session,rotateDeg){
let result = await this.#get(session);
if(!result)
return 0;// 已过期(或未加入验证池)
if(Math.abs(result.rotateDeg - rotateDeg) <= this.AllowMaxAngleDiff)
{
// 验证通过标识为已通过并设定有效期为60s
result.isPassed = true;
await RedisConnection.del('RotationVerificationService:' + session);
await RedisConnection.set('RotationVerificationService:' + session, JSON.stringify(result));
RedisConnection.expire('RotationVerificationService:' + session, this.ExpriedTimeSec);
return 1;
}
result.tryCount++;// 浪费一次尝试次数
if(result.tryCount >= this.AllowMaxTryCount)
{
this.#remove(session);
return -1;// 超过最大尝试次数
}
// 保存尝试次数
RedisConnection.set('RotationVerificationService:' + session, JSON.stringify(result));
return -2;// 角度差异过大
}
}
let _RotationVerificationService = new RotationVerificationService();
module.exports.RotationVerificationService = _RotationVerificationService;