系统:停服维护逻辑
This commit is contained in:
+2
-2
@@ -16,7 +16,7 @@ import * as redisService from './app/services/redisService';
|
|||||||
import './app/servers/user.rpc.define'
|
import './app/servers/user.rpc.define'
|
||||||
import * as routeUtil from './app/util/routeUtil';
|
import * as routeUtil from './app/util/routeUtil';
|
||||||
import { preload } from './preload';
|
import { preload } from './preload';
|
||||||
var checkEventFilter = require('./app/servers/battle/filter/checkEventFilter');
|
var globalFilter = require('./app/servers/connector/filter/global');
|
||||||
import { connectRedis } from './config/redis';
|
import { connectRedis } from './config/redis';
|
||||||
import * as timeTaskService from './app/services/timeTaskService';
|
import * as timeTaskService from './app/services/timeTaskService';
|
||||||
import * as redlockCacheService from './app/services/redlockCacheService';
|
import * as redlockCacheService from './app/services/redlockCacheService';
|
||||||
@@ -198,7 +198,7 @@ app.configure(ALL_ENVS, function () {
|
|||||||
app.filter(new pinus.filters.time());
|
app.filter(new pinus.filters.time());
|
||||||
app.filter(new pinus.filters.timeout());
|
app.filter(new pinus.filters.timeout());
|
||||||
|
|
||||||
app.filter(checkEventFilter(app));
|
app.filter(globalFilter(app));
|
||||||
|
|
||||||
// RPC 启用TCP协议
|
// RPC 启用TCP协议
|
||||||
app.set('proxyConfig', {
|
app.set('proxyConfig', {
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import {Application, RouteRecord, FrontendOrBackendSession, HandlerCallback} from "pinus";
|
|
||||||
import {checkEvent} from '../../../services/eventSercive';
|
|
||||||
import { refresh } from '../../../services/refreshService';
|
|
||||||
module.exports = function(app: Application) {
|
|
||||||
return new Filter(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
var Filter = function(this: any, app: Application) {
|
|
||||||
this.app = app;
|
|
||||||
};
|
|
||||||
|
|
||||||
Filter.prototype.before = function (routeRecord: RouteRecord, msg: any, session: FrontendOrBackendSession, next: HandlerCallback) {
|
|
||||||
|
|
||||||
next(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
Filter.prototype.after = function (err: Error, routeRecord: RouteRecord, msg: any, session: FrontendOrBackendSession, resp: any, next: HandlerCallback) {
|
|
||||||
//刷新
|
|
||||||
refresh(session);
|
|
||||||
return checkEvent(session).then(()=>{
|
|
||||||
next(err);
|
|
||||||
}).catch(e => {
|
|
||||||
console.error(e);
|
|
||||||
next(err);
|
|
||||||
}); // 奇遇本检查
|
|
||||||
};
|
|
||||||
@@ -239,4 +239,17 @@ export class ChatRemote {
|
|||||||
if (!channel) return;
|
if (!channel) return;
|
||||||
channel.pushMessage(path, resResult(STATUS.SUCCESS, data));
|
channel.pushMessage(path, resResult(STATUS.SUCCESS, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 全服推送维护
|
||||||
|
* @param serverId
|
||||||
|
*/
|
||||||
|
public async sendServerMaintenance(serverId: number) {
|
||||||
|
|
||||||
|
let roomId = groupRoomId(CHANNEL_PREFIX.WORLD, serverId);
|
||||||
|
let channel = this.channelService.getChannel(roomId, false);
|
||||||
|
if (!channel) return { result: false, serverId };
|
||||||
|
channel.pushMessage('onServerMaintenance', resResult(STATUS.SERVER_MAINTENANCE));
|
||||||
|
return { result: true, serverId };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import {Application, RouteRecord, FrontendOrBackendSession, HandlerCallback, pinus} from "pinus";
|
||||||
|
import { checkEvent } from '../../../services/eventSercive';
|
||||||
|
import { refresh } from '../../../services/refreshService';
|
||||||
|
import { getWorldChannelSid } from "../../../services/chatService";
|
||||||
|
import { resResult, checkWhiteListWithUid } from "../../../pubUtils/util";
|
||||||
|
import { STATUS } from "../../../consts";
|
||||||
|
import { UserModel } from "../../../db/User";
|
||||||
|
module.exports = function(app: Application) {
|
||||||
|
return new Filter(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
var Filter = function(this: any, app: Application) {
|
||||||
|
this.app = app;
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.before = async function (routeRecord: RouteRecord, msg: any, session: FrontendOrBackendSession, next: HandlerCallback) {
|
||||||
|
const serverId: number = session.get('serverId');
|
||||||
|
const sid: string = session.get('sid');
|
||||||
|
const roleId: string = session.get('roleId');
|
||||||
|
const uid: number = session.get('userid');
|
||||||
|
|
||||||
|
let connectorServers = pinus.app.getServersByType('connector');
|
||||||
|
let { id } = connectorServers[0];
|
||||||
|
let maintenServers = await pinus.app.rpc.connector.connectorRemote.getServerMainten.toServer(id);
|
||||||
|
if(maintenServers.indexOf(serverId) != -1) { // 该服务器维护
|
||||||
|
let isWhiteList = await checkWhiteListWithUid(uid);
|
||||||
|
if(isWhiteList) next(null);
|
||||||
|
|
||||||
|
pinus.app.get('channelService').pushMessageByUids('onServerMaintenance', resResult(STATUS.SERVER_MAINTENANCE), [{ uid: roleId, sid }]);
|
||||||
|
next(new Error(), resResult(STATUS.SERVER_MAINTENANCE));
|
||||||
|
}
|
||||||
|
|
||||||
|
next(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.after = function (err: Error, routeRecord: RouteRecord, msg: any, session: FrontendOrBackendSession, resp: any, next: HandlerCallback) {
|
||||||
|
//刷新
|
||||||
|
refresh(session);
|
||||||
|
return checkEvent(session).then(()=>{
|
||||||
|
next(err);
|
||||||
|
}).catch(e => {
|
||||||
|
console.error(e);
|
||||||
|
next(err);
|
||||||
|
}); // 奇遇本检查
|
||||||
|
};
|
||||||
@@ -118,6 +118,7 @@ export class EntryHandler {
|
|||||||
async addSession(role: RoleType, session: FrontendSession) {
|
async addSession(role: RoleType, session: FrontendSession) {
|
||||||
const self = this;
|
const self = this;
|
||||||
await session.abind(role.roleId);
|
await session.abind(role.roleId);
|
||||||
|
session.set('userid', role.userInfo.uid);
|
||||||
session.set('uid', role.roleId);
|
session.set('uid', role.roleId);
|
||||||
session.set('roleId', role.roleId);
|
session.set('roleId', role.roleId);
|
||||||
let updatedMailAt = role.updatedMailAt ? role.updatedMailAt : 0;
|
let updatedMailAt = role.updatedMailAt ? role.updatedMailAt : 0;
|
||||||
@@ -128,6 +129,7 @@ export class EntryHandler {
|
|||||||
session.set('serverId', role.serverId);
|
session.set('serverId', role.serverId);
|
||||||
session.set('funcs', role.funcs || []);
|
session.set('funcs', role.funcs || []);
|
||||||
session.set('guildCode', role.guildCode);
|
session.set('guildCode', role.guildCode);
|
||||||
|
session.push('userid', () => { });
|
||||||
session.push('sid', () => { });
|
session.push('sid', () => { });
|
||||||
session.push('roleId', () => { });
|
session.push('roleId', () => { });
|
||||||
session.push('roleName', () => { });
|
session.push('roleName', () => { });
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export class ConnectorRemote {
|
|||||||
this.channelService = app.get('channelService');
|
this.channelService = app.get('channelService');
|
||||||
}
|
}
|
||||||
private channelService: ChannelService;
|
private channelService: ChannelService;
|
||||||
|
private maintenServers: number[] = []; // 维护中的服务器
|
||||||
|
|
||||||
public async remoteLogin(uid: string) {
|
public async remoteLogin(uid: string) {
|
||||||
let sessionService = this.app.get('sessionService');
|
let sessionService = this.app.get('sessionService');
|
||||||
@@ -22,7 +23,7 @@ export class ConnectorRemote {
|
|||||||
let roleId = session.get('roleId');
|
let roleId = session.get('roleId');
|
||||||
let sid = session.get('sid');
|
let sid = session.get('sid');
|
||||||
let uids = [{uid: roleId, sid}];
|
let uids = [{uid: roleId, sid}];
|
||||||
this.channelService.pushMessageByUids('onRemoteLogin', resResult(STATUS.LOGIN_ERR ), uids);
|
this.channelService.pushMessageByUids('onRemoteLogin', resResult(STATUS.SERVER_MAINTENANCE ), uids);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await sessionService.akick(uid);
|
await sessionService.akick(uid);
|
||||||
@@ -42,4 +43,12 @@ export class ConnectorRemote {
|
|||||||
public async reloadResources() {
|
public async reloadResources() {
|
||||||
reloadResources();
|
reloadResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setServerMainten(maintenServers: number[]) {
|
||||||
|
return this.maintenServers = maintenServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getServerMainten() {
|
||||||
|
return this.maintenServers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import { SendMailFun } from '../../../services/mailService';
|
|||||||
import { GM_MAIL_TYPE } from '../../../consts';
|
import { GM_MAIL_TYPE } from '../../../consts';
|
||||||
import { RewardInter } from '../../../pubUtils/interface';
|
import { RewardInter } from '../../../pubUtils/interface';
|
||||||
import { MarqueeModel } from '../../../db/Marquee';
|
import { MarqueeModel } from '../../../db/Marquee';
|
||||||
|
import { ServerlistModel } from '../../../db/Serverlist';
|
||||||
|
import { getWorldChannelSid } from '../../../services/chatService';
|
||||||
export default function(app: Application) {
|
export default function(app: Application) {
|
||||||
return new GmHandler(app);
|
return new GmHandler(app);
|
||||||
}
|
}
|
||||||
@@ -183,4 +185,24 @@ export class GmHandler {
|
|||||||
}
|
}
|
||||||
return resResult(STATUS.SUCCESS);
|
return resResult(STATUS.SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendMaintenance(msg: { serverType: string }, session: BackendSession) {
|
||||||
|
const { serverType } = msg;
|
||||||
|
let allServers = await ServerlistModel.findByServerType(serverType);
|
||||||
|
let results = [];
|
||||||
|
for(let { serverId } of allServers) {
|
||||||
|
let chatSid = await getWorldChannelSid(serverId);
|
||||||
|
let result = await this.app.rpc.chat.chatRemote.sendServerMaintenance.toServer(chatSid, serverId);
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
return resResult(STATUS.SUCCESS, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setMaintenance() {
|
||||||
|
let connectorServers = pinus.app.getServersByType('connector');
|
||||||
|
for(let { id } of connectorServers) {
|
||||||
|
pinus.app.rpc.connector.connectorRemote.setServerMainten.toServer(id, [1]);
|
||||||
|
}
|
||||||
|
return resResult(STATUS.SUCCESS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ export class RoleHandler {
|
|||||||
await checkPvp(role);
|
await checkPvp(role);
|
||||||
|
|
||||||
return resResult(STATUS.SUCCESS, {
|
return resResult(STATUS.SUCCESS, {
|
||||||
roleId, roleName, heroes
|
roleId, roleName, ...heroes
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { resetPvpSeasonTime, setPvpDefResult, guildActivityStart, gateActivityEn
|
|||||||
import PvpDefenseType from '../../../db/PvpDefense';
|
import PvpDefenseType from '../../../db/PvpDefense';
|
||||||
import { DicGuildActivity } from '../../../pubUtils/dictionary/DicGuildActivity';
|
import { DicGuildActivity } from '../../../pubUtils/dictionary/DicGuildActivity';
|
||||||
import { reloadResources } from '../../../pubUtils/data';
|
import { reloadResources } from '../../../pubUtils/data';
|
||||||
import { setMarquee, cancelMarquee } from '../../../services/marqueeService';
|
import { setMarquee, cancelMarquee } from '../../../services/gmService';
|
||||||
|
|
||||||
export default function (app: Application) {
|
export default function (app: Application) {
|
||||||
return new SystimerRemote(app);
|
return new SystimerRemote(app);
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
import { MarqueeType, MarqueeModel } from "../db/Marquee";
|
import { MarqueeType, MarqueeModel } from "../db/Marquee";
|
||||||
import { MARQUEE_TYPE, STATUS } from "../consts";
|
import { MARQUEE_TYPE, STATUS, SERVER_STATUS } from "../consts";
|
||||||
import { scheduleJob, scheduledJobs, Job } from 'node-schedule';
|
import { scheduleJob, scheduledJobs, Job } from 'node-schedule';
|
||||||
import { createMarqueeMsg as sysCreateMarqueeMsg, pushMarqueeMsg as sysPushMarqueeMsg } from './sysChatService';
|
import { createMarqueeMsg as sysCreateMarqueeMsg, pushMarqueeMsg as sysPushMarqueeMsg } from './sysChatService';
|
||||||
import { GroupMessageType } from "../db/GroupMessage";
|
import { GroupMessageType } from "../db/GroupMessage";
|
||||||
import { resResult } from "../pubUtils/util";
|
import { resResult } from "../pubUtils/util";
|
||||||
|
import { MaintenanceModel, MaintenanceType } from '../db/Maintenance';
|
||||||
|
import { pinus } from "pinus";
|
||||||
|
import { getWorldChannelSid } from "./chatChannelService";
|
||||||
|
import { ServerlistModel } from "../db/Serverlist";
|
||||||
|
import { NoticeModel, NoticeType } from "../db/Notice";
|
||||||
|
|
||||||
|
// —————————————— 跑马灯 —————————————— //
|
||||||
|
// 初始
|
||||||
export async function initMarquee() {
|
export async function initMarquee() {
|
||||||
const marquees = await MarqueeModel.findEffectiveMarque();
|
const marquees = await MarqueeModel.findEffectiveMarque();
|
||||||
for(let marquee of marquees) {
|
for(let marquee of marquees) {
|
||||||
@@ -24,6 +31,9 @@ export async function setMarquee(code: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function generateMarqueeSchedule(marquee: MarqueeType) {
|
async function generateMarqueeSchedule(marquee: MarqueeType) {
|
||||||
|
let setSecondsMarquee = scheduledJobs[`setSeconds${marquee.code}`];
|
||||||
|
if(setSecondsMarquee) return false; // 已经在运行中了
|
||||||
|
|
||||||
if(marquee.type != MARQUEE_TYPE.SCHEDULE) return false;
|
if(marquee.type != MARQUEE_TYPE.SCHEDULE) return false;
|
||||||
if(marquee.startTime.getTime() >= marquee.endTime.getTime()) return false;
|
if(marquee.startTime.getTime() >= marquee.endTime.getTime()) return false;
|
||||||
if(marquee.endTime.getTime() < Date.now()) return false;
|
if(marquee.endTime.getTime() < Date.now()) return false;
|
||||||
@@ -89,3 +99,43 @@ export async function cancelMarquee(code: string) {
|
|||||||
await MarqueeModel.updateData(code, { isRunning: false });
|
await MarqueeModel.updateData(code, { isRunning: false });
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// —————————————— 停服维护 —————————————— //
|
||||||
|
// 维护信息
|
||||||
|
export async function initMaintenance() {
|
||||||
|
const maintenance = await MaintenanceModel.findOpenMaintenance();
|
||||||
|
if(maintenance) {
|
||||||
|
let { startTime, marquee } = maintenance;
|
||||||
|
if(marquee) await generateMarqueeSchedule(<MarqueeType>marquee);
|
||||||
|
if(Date.now() > startTime.getTime()) {
|
||||||
|
await startMaintenance(maintenance);
|
||||||
|
} else {
|
||||||
|
let startJob = scheduleJob(`startMainten${maintenance.code}`, startTime.getTime(), async () => {
|
||||||
|
await startMaintenance(maintenance, startJob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startMaintenance(maintenance: MaintenanceType, startJob?: Job) {
|
||||||
|
// 向全服发送
|
||||||
|
let { serverIds, notice } = maintenance;
|
||||||
|
for(let serverId of serverIds) {
|
||||||
|
let chatSid = await getWorldChannelSid(serverId);
|
||||||
|
await pinus.app.rpc.chat.chatRemote.sendServerMaintenance.toServer(chatSid, serverId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新serverlist上的status
|
||||||
|
await ServerlistModel.updateByServerIds(serverIds, { serverStatus: SERVER_STATUS.MAINTENANCE });
|
||||||
|
// 更新notice的isEnable
|
||||||
|
if(notice) {
|
||||||
|
await NoticeModel.updateNotice((<NoticeType>notice).id, { isEnable: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新connectorRemote里面的
|
||||||
|
let connectorServers = pinus.app.getServersByType('connector');
|
||||||
|
for(let { id } of connectorServers) {
|
||||||
|
pinus.app.rpc.connector.connectorRemote.setServerMainten.toServer(id, serverIds);
|
||||||
|
}
|
||||||
|
if(startJob) startJob.cancel();
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import { scheduleJob, Job } from 'node-schedule';
|
import { scheduleJob, Job, scheduledJobs } from 'node-schedule';
|
||||||
import { SystemConfigModel } from '../db/SystemConfig';
|
import { SystemConfigModel } from '../db/SystemConfig';
|
||||||
import PvpDefenseType,{ PvpDefenseModel } from '../db/PvpDefense';
|
import PvpDefenseType,{ PvpDefenseModel } from '../db/PvpDefense';
|
||||||
import { PVP } from '../pubUtils/dicParam';
|
import { PVP } from '../pubUtils/dicParam';
|
||||||
@@ -27,7 +27,7 @@ import { dispatch } from '../util/dispatcher';
|
|||||||
import { Rank } from './rankService';
|
import { Rank } from './rankService';
|
||||||
import { checkTask } from './taskService';
|
import { checkTask } from './taskService';
|
||||||
import { everydayRefresh } from './connectorService';
|
import { everydayRefresh } from './connectorService';
|
||||||
import { initMarquee } from './marqueeService';
|
import { initMarquee, initMaintenance } from './gmService';
|
||||||
|
|
||||||
const PER_SECOND = 1 * 1000;
|
const PER_SECOND = 1 * 1000;
|
||||||
const PER_DAY = 24 * 60 * 60;
|
const PER_DAY = 24 * 60 * 60;
|
||||||
@@ -87,6 +87,9 @@ export async function init() {
|
|||||||
|
|
||||||
// 跑马灯
|
// 跑马灯
|
||||||
await initMarquee();
|
await initMarquee();
|
||||||
|
|
||||||
|
// 维护信息
|
||||||
|
await initMaintenance();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPvpSeasonSchdule() {
|
function setPvpSeasonSchdule() {
|
||||||
|
|||||||
@@ -676,3 +676,15 @@ export enum MARQUEE_TYPE {
|
|||||||
INSTANT = 1, // 发送后立刻推送
|
INSTANT = 1, // 发送后立刻推送
|
||||||
SCHEDULE = 2, // 定时器
|
SCHEDULE = 2, // 定时器
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum SERVER_STATUS {
|
||||||
|
NEW = 1, // 新服
|
||||||
|
HOT = 2, // 火爆
|
||||||
|
WILL_OPEN = 3, // 即将开启
|
||||||
|
MAINTENANCE = 4, // 维护中
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WHITE_LIST_TYPE {
|
||||||
|
IP = 1, // ip地址
|
||||||
|
TEL = 2, // 玩家手机号
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ export const STATUS = {
|
|||||||
CONNECTOR_ERR: { code: 4, simStr: '连接服配置错误' },
|
CONNECTOR_ERR: { code: 4, simStr: '连接服配置错误' },
|
||||||
LOGIN_ERR: { code: 5, simStr: '检测到您的账号异地登录,已被迫下线' },
|
LOGIN_ERR: { code: 5, simStr: '检测到您的账号异地登录,已被迫下线' },
|
||||||
REDLOCK_ERR: { code: 6, simStr: 'redlock错误' },
|
REDLOCK_ERR: { code: 6, simStr: 'redlock错误' },
|
||||||
|
SERVER_MAINTENANCE: { code: 7, simStr: '服务器维护中' },
|
||||||
GLOBAL_ERR: { code: 1003, simStr: '服务器内部错误' },
|
GLOBAL_ERR: { code: 1003, simStr: '服务器内部错误' },
|
||||||
|
|
||||||
// 账号相关状态 10000 - 19999
|
// 账号相关状态 10000 - 19999
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import BaseModel from './BaseModel';
|
||||||
|
import { index, getModelForClass, prop, DocumentType, mongoose, Ref } from '@typegoose/typegoose';
|
||||||
|
import Marquee from './Marquee';
|
||||||
|
import Notice from './Notice';
|
||||||
|
import GMMail from './GMMail';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开服策略
|
||||||
|
*/
|
||||||
|
@index({ id: 1 })
|
||||||
|
|
||||||
|
export default class Maintenance extends BaseModel {
|
||||||
|
|
||||||
|
@prop({ required: true })
|
||||||
|
code: string; // 策略唯一标志
|
||||||
|
|
||||||
|
@prop({ required: true, type: Number })
|
||||||
|
serverIds: number[]; // 服务器类型
|
||||||
|
|
||||||
|
@prop({ required: true })
|
||||||
|
isOpen: boolean; // 维护是否使用
|
||||||
|
|
||||||
|
@prop({ required: true })
|
||||||
|
startTime: Date; // 维护开始时间
|
||||||
|
|
||||||
|
@prop({ ref: 'Marquee', type: mongoose.Schema.Types.ObjectId })
|
||||||
|
marquee: Ref<Marquee>; // 跑马灯,连接到marquee表
|
||||||
|
|
||||||
|
@prop({ ref: 'Notice', type: mongoose.Schema.Types.ObjectId })
|
||||||
|
notice: Ref<Notice>; // 维护公告
|
||||||
|
|
||||||
|
@prop({ ref: 'Notice', type: mongoose.Schema.Types.ObjectId })
|
||||||
|
mail: Ref<GMMail>; // 维护结束邮件
|
||||||
|
|
||||||
|
|
||||||
|
public static async findOpenMaintenance() {
|
||||||
|
const rec: MaintenanceType = await MaintenanceModel.findOne({ isOpen: true }, { _id: 0 }).populate('marquee').populate('notice').lean();
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async findByCode(code: string) {
|
||||||
|
const rec: MaintenanceType = await MaintenanceModel.findOne({ code }).lean();
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MaintenanceModel = getModelForClass(Maintenance);
|
||||||
|
|
||||||
|
export interface MaintenanceType extends Pick<DocumentType<Maintenance>, keyof Maintenance> { }
|
||||||
|
export type MaintenanceTypeParam = Partial<MaintenanceType>; // 将所有字段变成可选项
|
||||||
@@ -2,7 +2,7 @@ import BaseModel from './BaseModel';
|
|||||||
import { index, getModelForClass, prop, DocumentType, modelOptions } from '@typegoose/typegoose';
|
import { index, getModelForClass, prop, DocumentType, modelOptions } from '@typegoose/typegoose';
|
||||||
import { GameModel } from './Game';
|
import { GameModel } from './Game';
|
||||||
import { CounterModel } from './Counter';
|
import { CounterModel } from './Counter';
|
||||||
import { COUNTER } from '../consts';
|
import { COUNTER, SERVER_STATUS } from '../consts';
|
||||||
import { ServerStategyTypeParam } from './ServerStategy';
|
import { ServerStategyTypeParam } from './ServerStategy';
|
||||||
import { getServerName, getServerGroupName } from '../pubUtils/data';
|
import { getServerName, getServerGroupName } from '../pubUtils/data';
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ export default class Serverlist extends BaseModel {
|
|||||||
@prop({ required: false, default: 3014 })
|
@prop({ required: false, default: 3014 })
|
||||||
port: number; // pinus端口
|
port: number; // pinus端口
|
||||||
|
|
||||||
@prop({ required: true, default: 1 })
|
@prop({ required: true, default: 1, enum: SERVER_STATUS })
|
||||||
serverStatus: number; // 服务器状态
|
serverStatus: number; // 服务器状态
|
||||||
|
|
||||||
@prop({ required: true, type: Number })
|
@prop({ required: true, type: Number })
|
||||||
@@ -50,7 +50,7 @@ export default class Serverlist extends BaseModel {
|
|||||||
if (now > this.openTime) {
|
if (now > this.openTime) {
|
||||||
return this.serverStatus;
|
return this.serverStatus;
|
||||||
} else {
|
} else {
|
||||||
return 3; // 未开服
|
return SERVER_STATUS.WILL_OPEN; // 未开服
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +81,11 @@ export default class Serverlist extends BaseModel {
|
|||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async updateByServerIds(serverIds: number[], update: ServerlistUpdate) {
|
||||||
|
const result = await ServerlistModel.updateMany({ id: { $in: serverIds } }, { $set: update });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public static async updateGroupName(groupId: number, groupName: string) {
|
public static async updateGroupName(groupId: number, groupName: string) {
|
||||||
let server = await ServerlistModel.updateMany({ groupId }, { $set: { groupName } });
|
let server = await ServerlistModel.updateMany({ groupId }, { $set: { groupName } });
|
||||||
return server;
|
return server;
|
||||||
@@ -175,6 +180,7 @@ export default class Serverlist extends BaseModel {
|
|||||||
let result = await ServerlistModel.updateMany({ activityGroupId: { $elemMatch: { $eq: groupId } }}, { $pull: { activityGroupId: groupId } }, { new: true }).lean(true);
|
let result = await ServerlistModel.updateMany({ activityGroupId: { $elemMatch: { $eq: groupId } }}, { $pull: { activityGroupId: groupId } }, { new: true }).lean(true);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ServerlistModel = getModelForClass(Serverlist);
|
export const ServerlistModel = getModelForClass(Serverlist);
|
||||||
|
|||||||
+8
-8
@@ -105,7 +105,7 @@ export default class User extends BaseModel {
|
|||||||
@prop({ required: false, default: "" })
|
@prop({ required: false, default: "" })
|
||||||
getuiCID: string;
|
getuiCID: string;
|
||||||
|
|
||||||
public static async createUser(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, guestTime: number = 0) {
|
public static async createUser(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, guestTime: number = 0, ip: string = '') {
|
||||||
let _tel = aesEncryptcfb(tel, ENCRYPT_KEY, ENCRYPT_IV);
|
let _tel = aesEncryptcfb(tel, ENCRYPT_KEY, ENCRYPT_IV);
|
||||||
const curTime: Date = new Date();
|
const curTime: Date = new Date();
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ export default class User extends BaseModel {
|
|||||||
const userCode = genCode(8);
|
const userCode = genCode(8);
|
||||||
const doc = new UserModel();
|
const doc = new UserModel();
|
||||||
let update = {};
|
let update = {};
|
||||||
update = Object.assign(update, doc.toJSON(), { platform, pkgName, serverType, createTime: curTime, uid, userCode, username: `用户${uid}`, isGuest, token, lastLoginTime: curTime, guestTime });
|
update = Object.assign(update, doc.toJSON(), { platform, pkgName, serverType, createTime: curTime, uid, userCode, username: `用户${uid}`, isGuest, token, lastLoginTime: curTime, guestTime, ip });
|
||||||
if (isGuest) update["guestId"] = _tel;
|
if (isGuest) update["guestId"] = _tel;
|
||||||
delete update["device"];
|
delete update["device"];
|
||||||
|
|
||||||
@@ -126,11 +126,11 @@ export default class User extends BaseModel {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async updateToken(tel: string, token: string, deviceId: string) {
|
public static async updateToken(tel: string, token: string, deviceId: string, ip: string) {
|
||||||
|
|
||||||
const curTime: Date = new Date();
|
const curTime: Date = new Date();
|
||||||
|
|
||||||
let user = await UserModel.findOneAndUpdate({ tel }, { $set: { token, lastLoginTime: curTime }, $addToSet: { device: deviceId } }, { new: true }).lean({ getters: true });
|
let user = await UserModel.findOneAndUpdate({ tel }, { $set: { token, lastLoginTime: curTime, ip }, $addToSet: { device: deviceId } }, { new: true }).lean({ getters: true });
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,14 +139,14 @@ export default class User extends BaseModel {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async createOrUpdate(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string) {
|
public static async createOrUpdate(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, ip: string) {
|
||||||
// console.log(tel);
|
// console.log(tel);
|
||||||
|
|
||||||
let user: UserType = await UserModel.findOne({ tel }).lean({ getters: true });
|
let user: UserType = await UserModel.findOne({ tel }).lean({ getters: true });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = await UserModel.createUser(isGuest, tel, token, platform, pkgName, serverType, deviceId);
|
user = await UserModel.createUser(isGuest, tel, token, platform, pkgName, serverType, deviceId, 0, ip);
|
||||||
} else {
|
} else {
|
||||||
user = await UserModel.updateToken(tel, token, deviceId);
|
user = await UserModel.updateToken(tel, token, deviceId, ip);
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
@@ -185,7 +185,7 @@ export default class User extends BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async findUserByToken(token: string) {
|
public static async findUserByToken(token: string) {
|
||||||
const user: UserType = await UserModel.findOne({ token }).select('uid token serverType auth tel userCode pkgName').lean({ getters: true });
|
const user: UserType = await UserModel.findOne({ token }).select('uid token serverType auth tel userCode pkgName ip').lean({ getters: true });
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import BaseModel from './BaseModel';
|
||||||
|
import { getModelForClass, prop, DocumentType } from '@typegoose/typegoose';
|
||||||
|
import { WHITE_LIST_TYPE } from '../consts';
|
||||||
|
|
||||||
|
export default class WhiteList extends BaseModel {
|
||||||
|
|
||||||
|
@prop({ required: true, enum: WHITE_LIST_TYPE })
|
||||||
|
type: WHITE_LIST_TYPE; // 区号
|
||||||
|
|
||||||
|
@prop({ required: true })
|
||||||
|
str: string; // ip或手机号
|
||||||
|
|
||||||
|
public static async checkIp(ip: string) {
|
||||||
|
const rec: WhiteListModelType = await WhiteListModel.findOne({ type: WHITE_LIST_TYPE.IP, str: ip }).lean();
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async checkTel(tel: string) {
|
||||||
|
const rec: WhiteListModelType = await WhiteListModel.findOne({ type: WHITE_LIST_TYPE.TEL, str: tel }).lean();
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WhiteListModel = getModelForClass(WhiteList);
|
||||||
|
|
||||||
|
export interface WhiteListModelType extends Pick<DocumentType<WhiteList>, keyof WhiteList> { }
|
||||||
|
export type WhiteListModelTypeParam = Partial<WhiteListModelType>; // 将所有字段变成可选项
|
||||||
@@ -9,6 +9,8 @@ import { HERO_CE_RATIO, ABI_STAGE, GACHA_TO_FLOOR, REFRESH_TIME } from '../const
|
|||||||
import { findIndex } from 'underscore';
|
import { findIndex } from 'underscore';
|
||||||
import { getTimeFunM } from './timeUtil';
|
import { getTimeFunM } from './timeUtil';
|
||||||
import { Floor } from '../domain/activityField/gachaField';
|
import { Floor } from '../domain/activityField/gachaField';
|
||||||
|
import { WhiteListModel } from '../db/WhiteList';
|
||||||
|
import { UserModel } from '../db/User';
|
||||||
const randomName = require("chinese-random-name");
|
const randomName = require("chinese-random-name");
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
@@ -581,3 +583,25 @@ export function getFloorStatus(gachaId: number, floor: Floor[]) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function checkWhiteListWithUid(uid: number) {
|
||||||
|
let user = await UserModel.findUserByUid(uid);
|
||||||
|
if(!user) return false;
|
||||||
|
let { tel, ip, auth } = user;
|
||||||
|
return await checkWhiteList(tel, ip, auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkWhiteList(tel: string, ip: string, auth: number) {
|
||||||
|
if(auth == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(tel) {
|
||||||
|
let result = await WhiteListModel.checkTel(tel);
|
||||||
|
if(!!result) return true;
|
||||||
|
}
|
||||||
|
if(ip) {
|
||||||
|
let result = await WhiteListModel.checkIp(ip);
|
||||||
|
if(!!result) return true;
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -27,6 +27,8 @@ export default class FooBoot implements IBoot {
|
|||||||
async willReady() {
|
async willReady() {
|
||||||
// All plugins have started, can do some thing before app ready.
|
// All plugins have started, can do some thing before app ready.
|
||||||
// await this.customLoadModel();
|
// await this.customLoadModel();
|
||||||
|
const ctx = await this.app.createAnonymousContext();
|
||||||
|
await ctx.service.game.readDbMaintenance();
|
||||||
}
|
}
|
||||||
|
|
||||||
async didReady() {
|
async didReady() {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { STATUS } from '@consts';
|
||||||
|
import { checkWhiteList } from 'app/pubUtils/util';
|
||||||
|
|
||||||
|
module.exports = () => {
|
||||||
|
return async function checkMainten(ctx, next) {
|
||||||
|
const { serverId } = ctx.request.body;
|
||||||
|
if (serverId) {
|
||||||
|
let maintenServers = ctx.service.game.getServerMainten();
|
||||||
|
if (maintenServers.indexOf(serverId) != -1) {
|
||||||
|
let isWhiteList = await checkWhiteList(ctx.tel, ctx.request.ip, ctx.auth);
|
||||||
|
if (isWhiteList) {
|
||||||
|
await next();
|
||||||
|
} else {
|
||||||
|
ctx.body = ctx.service.utils.resResult(STATUS.SERVER_MAINTENANCE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await next();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ module.exports = () => {
|
|||||||
ctx.auth = user.auth;
|
ctx.auth = user.auth;
|
||||||
ctx.userCode = user.userCode;
|
ctx.userCode = user.userCode;
|
||||||
ctx.pkgName = user.pkgName;
|
ctx.pkgName = user.pkgName;
|
||||||
|
ctx.tel = user.tel;
|
||||||
await next();
|
await next();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Application } from 'egg';
|
|||||||
export default (app: Application) => {
|
export default (app: Application) => {
|
||||||
const { controller, router } = app;
|
const { controller, router } = app;
|
||||||
const tokenParser = app.middleware.tokenParser();
|
const tokenParser = app.middleware.tokenParser();
|
||||||
|
const checkMainten = app.middleware.checkMainten();
|
||||||
router.get('/dev', controller.home.dev);
|
router.get('/dev', controller.home.dev);
|
||||||
router.get('/dev/smscode', controller.account.getSmsCode);
|
router.get('/dev/smscode', controller.account.getSmsCode);
|
||||||
router.get('/', controller.home.index);
|
router.get('/', controller.home.index);
|
||||||
@@ -11,8 +12,8 @@ export default (app: Application) => {
|
|||||||
router.post('/user/smslogin', controller.account.smsLogin);
|
router.post('/user/smslogin', controller.account.smsLogin);
|
||||||
router.post('/user/setpassword', tokenParser, controller.account.setPassword);
|
router.post('/user/setpassword', tokenParser, controller.account.setPassword);
|
||||||
router.post('/user/pwlogin', controller.account.pwLogin);
|
router.post('/user/pwlogin', controller.account.pwLogin);
|
||||||
router.post('/user/checkrole', tokenParser, controller.account.checkRole);
|
router.post('/user/checkrole', tokenParser, checkMainten, controller.account.checkRole);
|
||||||
router.post('/user/createrole', tokenParser, controller.account.createRole);
|
router.post('/user/createrole', tokenParser, checkMainten, controller.account.createRole);
|
||||||
router.post('/user/bind', tokenParser, controller.account.bind);
|
router.post('/user/bind', tokenParser, controller.account.bind);
|
||||||
router.post('/user/authentication', tokenParser, controller.account.authentication);
|
router.post('/user/authentication', tokenParser, controller.account.authentication);
|
||||||
router.post('/game/getserverlist', tokenParser, controller.game.getServerList);
|
router.post('/game/getserverlist', tokenParser, controller.game.getServerList);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export default class Auth extends Service {
|
|||||||
|
|
||||||
public async deviceLogin(isGuest: boolean, token: string, deviceId: string, platform: string, pkgName: string, serverType: string, getuiCID: string) {
|
public async deviceLogin(isGuest: boolean, token: string, deviceId: string, platform: string, pkgName: string, serverType: string, getuiCID: string) {
|
||||||
const ctx = this.ctx;
|
const ctx = this.ctx;
|
||||||
|
const ip = ctx.request.ip;
|
||||||
|
|
||||||
let user = await UserModel.findUserByToken(token);
|
let user = await UserModel.findUserByToken(token);
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ export default class Auth extends Service {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
user = await UserModel.createUser(isGuest, tel, token, platform, pkgName, serverType, deviceId);
|
user = await UserModel.createUser(isGuest, tel, token, platform, pkgName, serverType, deviceId, 0, ip);
|
||||||
if (getuiCID) {//更新个推cid
|
if (getuiCID) {//更新个推cid
|
||||||
await UserModel.updateGetuiCID(tel, getuiCID);
|
await UserModel.updateGetuiCID(tel, getuiCID);
|
||||||
}
|
}
|
||||||
@@ -61,7 +62,7 @@ export default class Auth extends Service {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const token = ctx.service.utils.generateStr(256);
|
const token = ctx.service.utils.generateStr(256);
|
||||||
user = await UserModel.updateToken(user.tel, token, deviceId);
|
user = await UserModel.updateToken(user.tel, token, deviceId, ip);
|
||||||
if (getuiCID) {//更新个推cid
|
if (getuiCID) {//更新个推cid
|
||||||
await UserModel.updateGetuiCID(user.tel, getuiCID);
|
await UserModel.updateGetuiCID(user.tel, getuiCID);
|
||||||
}
|
}
|
||||||
@@ -187,6 +188,7 @@ export default class Auth extends Service {
|
|||||||
*/
|
*/
|
||||||
public async smsLogin(tel: string, deviceId: string, code: string, platform: string, pkgName: string, serverType: string, getuiCID: string) {
|
public async smsLogin(tel: string, deviceId: string, code: string, platform: string, pkgName: string, serverType: string, getuiCID: string) {
|
||||||
const ctx = this.ctx;
|
const ctx = this.ctx;
|
||||||
|
const ip = ctx.request.ip;
|
||||||
// 参数检查
|
// 参数检查
|
||||||
const telVerify = this.checkTelNo(tel);
|
const telVerify = this.checkTelNo(tel);
|
||||||
if (telVerify.status !== 0) {
|
if (telVerify.status !== 0) {
|
||||||
@@ -216,7 +218,7 @@ export default class Auth extends Service {
|
|||||||
|
|
||||||
// 用户注册登录
|
// 用户注册登录
|
||||||
const token = ctx.service.utils.generateStr(256);
|
const token = ctx.service.utils.generateStr(256);
|
||||||
const user = await UserModel.createOrUpdate(false, tel, token, platform, pkgName, serverType, deviceId);
|
const user = await UserModel.createOrUpdate(false, tel, token, platform, pkgName, serverType, deviceId, ip);
|
||||||
if (getuiCID) {//更新个推cid
|
if (getuiCID) {//更新个推cid
|
||||||
await UserModel.updateGetuiCID(tel, getuiCID);
|
await UserModel.updateGetuiCID(tel, getuiCID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
import { Service } from 'egg';
|
||||||
|
import { MaintenanceModel } from '@db/Maintenance';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Service
|
||||||
|
*/
|
||||||
|
export default class Game extends Service {
|
||||||
|
|
||||||
|
public setServerMainten(maintenServers: number[]) {
|
||||||
|
return this.app['maintenServers'] = maintenServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getServerMainten() {
|
||||||
|
return this.app['maintenServers']||[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async readDbMaintenance() {
|
||||||
|
const maintenance = await MaintenanceModel.findOpenMaintenance();
|
||||||
|
if(maintenance) {
|
||||||
|
this.setServerMainten(maintenance.serverIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,12 +2,14 @@
|
|||||||
// Do not modify this file!!!!!!!!!
|
// Do not modify this file!!!!!!!!!
|
||||||
|
|
||||||
import 'egg';
|
import 'egg';
|
||||||
|
import ExportCheckMainten from '../../../app/middleware/checkMainten';
|
||||||
import ExportGmTokenParser from '../../../app/middleware/gmTokenParser';
|
import ExportGmTokenParser from '../../../app/middleware/gmTokenParser';
|
||||||
import ExportParmsDecode from '../../../app/middleware/parmsDecode';
|
import ExportParmsDecode from '../../../app/middleware/parmsDecode';
|
||||||
import ExportTokenParser from '../../../app/middleware/tokenParser';
|
import ExportTokenParser from '../../../app/middleware/tokenParser';
|
||||||
|
|
||||||
declare module 'egg' {
|
declare module 'egg' {
|
||||||
interface IMiddleware {
|
interface IMiddleware {
|
||||||
|
checkMainten: typeof ExportCheckMainten;
|
||||||
gmTokenParser: typeof ExportGmTokenParser;
|
gmTokenParser: typeof ExportGmTokenParser;
|
||||||
parmsDecode: typeof ExportParmsDecode;
|
parmsDecode: typeof ExportParmsDecode;
|
||||||
tokenParser: typeof ExportTokenParser;
|
tokenParser: typeof ExportTokenParser;
|
||||||
|
|||||||
+2
@@ -7,6 +7,7 @@ type AnyFunc<T = any> = (...args: any[]) => T;
|
|||||||
type CanExportFunc = AnyFunc<Promise<any>> | AnyFunc<IterableIterator<any>>;
|
type CanExportFunc = AnyFunc<Promise<any>> | AnyFunc<IterableIterator<any>>;
|
||||||
type AutoInstanceType<T, U = T extends CanExportFunc ? T : T extends AnyFunc ? ReturnType<T> : T> = U extends AnyClass ? InstanceType<U> : U;
|
type AutoInstanceType<T, U = T extends CanExportFunc ? T : T extends AnyFunc ? ReturnType<T> : T> = U extends AnyClass ? InstanceType<U> : U;
|
||||||
import ExportAuth from '../../../app/service/Auth';
|
import ExportAuth from '../../../app/service/Auth';
|
||||||
|
import ExportGame from '../../../app/service/Game';
|
||||||
import ExportTest from '../../../app/service/Test';
|
import ExportTest from '../../../app/service/Test';
|
||||||
import ExportTurboCore from '../../../app/service/TurboCore';
|
import ExportTurboCore from '../../../app/service/TurboCore';
|
||||||
import ExportUtils from '../../../app/service/Utils';
|
import ExportUtils from '../../../app/service/Utils';
|
||||||
@@ -14,6 +15,7 @@ import ExportUtils from '../../../app/service/Utils';
|
|||||||
declare module 'egg' {
|
declare module 'egg' {
|
||||||
interface IService {
|
interface IService {
|
||||||
auth: AutoInstanceType<typeof ExportAuth>;
|
auth: AutoInstanceType<typeof ExportAuth>;
|
||||||
|
game: AutoInstanceType<typeof ExportGame>;
|
||||||
test: AutoInstanceType<typeof ExportTest>;
|
test: AutoInstanceType<typeof ExportTest>;
|
||||||
turboCore: AutoInstanceType<typeof ExportTurboCore>;
|
turboCore: AutoInstanceType<typeof ExportTurboCore>;
|
||||||
utils: AutoInstanceType<typeof ExportUtils>;
|
utils: AutoInstanceType<typeof ExportUtils>;
|
||||||
|
|||||||
Reference in New Issue
Block a user