ts-game-decorators 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ export declare const createRedisAdapter: () => Promise<any>;
2
+ export declare const getRedisAdapter: () => any;
3
+ export declare const getRedisClients: () => {
4
+ pubClient: any;
5
+ subClient: any;
6
+ };
7
+ export declare const closeRedisConnections: () => Promise<void>;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.closeRedisConnections = exports.getRedisClients = exports.getRedisAdapter = exports.createRedisAdapter = void 0;
7
+ const redis_1 = require("redis");
8
+ const redis_adapter_1 = require("@socket.io/redis-adapter");
9
+ const dotenv_1 = __importDefault(require("dotenv"));
10
+ dotenv_1.default.config();
11
+ const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
12
+ const REDIS_USERNAME = process.env.REDIS_USERNAME;
13
+ const REDIS_PASSWORD = process.env.REDIS_PASSWORD;
14
+ // Redis client instances for reuse
15
+ let pubClient = null;
16
+ let subClient = null;
17
+ let redisAdapter = null;
18
+ const createRedisAdapter = async () => {
19
+ try {
20
+ console.log('🔗 Connecting to Redis...');
21
+ // Build Redis client configuration
22
+ const redisConfig = {
23
+ url: REDIS_URL,
24
+ socket: {
25
+ reconnectStrategy: (retries) => {
26
+ if (retries > 10) {
27
+ console.error('❌ Redis: Too many retries, giving up');
28
+ return new Error('Too many retries');
29
+ }
30
+ console.log(`🔄 Redis: Retrying connection (${retries}/10)`);
31
+ return Math.min(retries * 100, 3000);
32
+ }
33
+ }
34
+ };
35
+ // Only add authentication if credentials are provided
36
+ if (REDIS_USERNAME) {
37
+ redisConfig.username = REDIS_USERNAME;
38
+ }
39
+ if (REDIS_PASSWORD) {
40
+ redisConfig.password = REDIS_PASSWORD;
41
+ }
42
+ console.log(`🔗 Redis config: ${REDIS_URL} ${REDIS_USERNAME ? `(username: ${REDIS_USERNAME})` : ''} ${REDIS_PASSWORD ? '(password: ***)' : '(no auth)'}`);
43
+ // Create publisher client
44
+ pubClient = (0, redis_1.createClient)(redisConfig);
45
+ // Create subscriber client (duplicate of publisher)
46
+ subClient = pubClient.duplicate();
47
+ // Error handling for publisher
48
+ pubClient.on('error', (err) => {
49
+ console.error('❌ Redis Publisher Error:', err);
50
+ });
51
+ pubClient.on('connect', () => {
52
+ console.log('✅ Redis Publisher connected');
53
+ });
54
+ pubClient.on('reconnecting', () => {
55
+ console.log('🔄 Redis Publisher reconnecting...');
56
+ });
57
+ // Error handling for subscriber
58
+ subClient.on('error', (err) => {
59
+ console.error('❌ Redis Subscriber Error:', err);
60
+ });
61
+ subClient.on('connect', () => {
62
+ console.log('✅ Redis Subscriber connected');
63
+ });
64
+ subClient.on('reconnecting', () => {
65
+ console.log('🔄 Redis Subscriber reconnecting...');
66
+ });
67
+ // Connect both clients
68
+ await Promise.all([pubClient.connect(), subClient.connect()]);
69
+ // Create Socket.IO Redis adapter
70
+ redisAdapter = (0, redis_adapter_1.createAdapter)(pubClient, subClient, {
71
+ key: 'socket.io',
72
+ requestsTimeout: 5000
73
+ });
74
+ console.log('✅ Redis adapter created successfully');
75
+ return redisAdapter;
76
+ }
77
+ catch (error) {
78
+ console.error('❌ Failed to create Redis adapter:', error);
79
+ throw error;
80
+ }
81
+ };
82
+ exports.createRedisAdapter = createRedisAdapter;
83
+ // Function to get Redis adapter instance
84
+ const getRedisAdapter = () => {
85
+ return redisAdapter;
86
+ };
87
+ exports.getRedisAdapter = getRedisAdapter;
88
+ // Function to get Redis clients for direct operations
89
+ const getRedisClients = () => {
90
+ return { pubClient, subClient };
91
+ };
92
+ exports.getRedisClients = getRedisClients;
93
+ // Graceful shutdown for Redis connections
94
+ const closeRedisConnections = async () => {
95
+ try {
96
+ console.log('🛑 Closing Redis connections...');
97
+ if (pubClient && pubClient.isOpen) {
98
+ try {
99
+ await pubClient.quit();
100
+ console.log('✅ Redis Publisher connection closed');
101
+ }
102
+ catch (error) {
103
+ console.error('❌ Error closing Redis Publisher connection:', error);
104
+ }
105
+ }
106
+ else if (pubClient) {
107
+ console.log('⚠️ Redis Publisher was already closed');
108
+ }
109
+ if (subClient && subClient.isOpen) {
110
+ try {
111
+ await subClient.quit();
112
+ console.log('✅ Redis Subscriber connection closed');
113
+ }
114
+ catch (error) {
115
+ console.error('❌ Error closing Redis Subscriber connection:', error);
116
+ }
117
+ }
118
+ else if (subClient) {
119
+ console.log('⚠️ Redis Subscriber was already closed');
120
+ }
121
+ }
122
+ catch (error) {
123
+ console.error('❌ Error closing Redis connections:', error);
124
+ }
125
+ };
126
+ exports.closeRedisConnections = closeRedisConnections;
@@ -0,0 +1,4 @@
1
+ import { Collection } from 'couchbase';
2
+ export declare function connectToCouchbase(): Promise<void>;
3
+ export declare function queryData(query: string): Promise<any[]>;
4
+ export declare function getCouchbaseUsersCollection(): Collection;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.connectToCouchbase = connectToCouchbase;
7
+ exports.queryData = queryData;
8
+ exports.getCouchbaseUsersCollection = getCouchbaseUsersCollection;
9
+ const couchbase_1 = require("couchbase");
10
+ const dotenv_1 = __importDefault(require("dotenv"));
11
+ dotenv_1.default.config();
12
+ let cluster;
13
+ let bucket;
14
+ let dataCollection;
15
+ async function connectToCouchbase() {
16
+ try {
17
+ // Kết nối với Couchbase Cluster
18
+ cluster = await couchbase_1.Cluster.connect(`couchbase://${process.env.COUCHBASE_URL || '165.22.240.52'}`, {
19
+ username: process.env.COUCHBASE_USERNAME || 'nhat.huy.7996@gmail.com',
20
+ password: process.env.COUCHBASE_PASSWORD || 'oc3qKtHXELXL',
21
+ });
22
+ // Lấy bucket từ cluster
23
+ bucket = cluster.bucket(process.env.COUCHBASE_BUCKET || 'buildAnArmy');
24
+ //Get collection
25
+ dataCollection = bucket.collection('users');
26
+ console.log('✅ Connected to Couchbase');
27
+ }
28
+ catch (err) {
29
+ console.error('❌ Failed to connect to Couchbase:', err);
30
+ }
31
+ }
32
+ /*
33
+ * query Data from couchbase
34
+ * @param query: string
35
+ * @returns array of rows
36
+ */
37
+ async function queryData(query) {
38
+ try {
39
+ const result = await cluster.query(query);
40
+ return result.rows; // Return array of rows
41
+ }
42
+ catch (err) {
43
+ console.error('Error executing query:', err);
44
+ throw new Error('Failed to fetch top users');
45
+ }
46
+ }
47
+ function getCouchbaseUsersCollection() {
48
+ if (!dataCollection) {
49
+ throw new Error('Couchbase is not connected. Call connectToCouchbase first.');
50
+ }
51
+ return dataCollection;
52
+ }
@@ -0,0 +1,5 @@
1
+ import { Low } from 'lowdb';
2
+ interface Data {
3
+ }
4
+ export declare const db: Low<Data>;
5
+ export {};
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.db = void 0;
4
+ const lowdb_1 = require("lowdb");
5
+ const node_1 = require("lowdb/node");
6
+ // File lưu dữ liệu
7
+ const adapter = new node_1.JSONFile("db.json");
8
+ exports.db = new lowdb_1.Low(adapter, { users: [] });
@@ -0,0 +1,7 @@
1
+ import 'reflect-metadata';
2
+ import { Express } from 'express';
3
+ export declare function RouterController(basePath?: string): ClassDecorator;
4
+ export declare function Get(path: string): MethodDecorator;
5
+ export declare function Post(path: string): MethodDecorator;
6
+ export declare function Authen(): MethodDecorator & ClassDecorator;
7
+ export declare function registerRoutes(app: Express, authMiddleware: any, ...controllers: any[]): void;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RouterController = RouterController;
4
+ exports.Get = Get;
5
+ exports.Post = Post;
6
+ exports.Authen = Authen;
7
+ exports.registerRoutes = registerRoutes;
8
+ require("reflect-metadata");
9
+ const express_1 = require("express");
10
+ const ROUTER_META = Symbol('router');
11
+ const ROUTES_META = Symbol('routes');
12
+ const AUTHEN_META = Symbol('authen');
13
+ function RouterController(basePath = '') {
14
+ return (target) => {
15
+ Reflect.defineMetadata(ROUTER_META, basePath, target);
16
+ };
17
+ }
18
+ function Get(path) {
19
+ return (target, propertyKey) => {
20
+ const routes = Reflect.getMetadata(ROUTES_META, target.constructor) || [];
21
+ routes.push({ method: 'get', path, handler: propertyKey });
22
+ Reflect.defineMetadata(ROUTES_META, routes, target.constructor);
23
+ };
24
+ }
25
+ function Post(path) {
26
+ return (target, propertyKey) => {
27
+ const routes = Reflect.getMetadata(ROUTES_META, target.constructor) || [];
28
+ routes.push({ method: 'post', path, handler: propertyKey });
29
+ Reflect.defineMetadata(ROUTES_META, routes, target.constructor);
30
+ };
31
+ }
32
+ function Authen() {
33
+ return (target, propertyKey) => {
34
+ if (propertyKey) {
35
+ // Method
36
+ const authenMethods = Reflect.getMetadata(AUTHEN_META, target.constructor) || [];
37
+ authenMethods.push(propertyKey);
38
+ Reflect.defineMetadata(AUTHEN_META, authenMethods, target.constructor);
39
+ }
40
+ else {
41
+ // Class
42
+ Reflect.defineMetadata(AUTHEN_META, true, target);
43
+ }
44
+ };
45
+ }
46
+ function registerRoutes(app, authMiddleware, ...controllers) {
47
+ controllers.forEach(Controller => {
48
+ const basePath = Reflect.getMetadata(ROUTER_META, Controller) || '';
49
+ const routes = Reflect.getMetadata(ROUTES_META, Controller) || [];
50
+ const classAuthen = Reflect.getMetadata(AUTHEN_META, Controller);
51
+ const instance = new Controller();
52
+ const router = (0, express_1.Router)();
53
+ routes.forEach((route) => {
54
+ const handler = (req, res, next) => {
55
+ Promise.resolve(instance[route.handler](req, res, next)).catch(next);
56
+ };
57
+ const methodAuthenArr = Reflect.getMetadata(AUTHEN_META, Controller) || [];
58
+ const methodAuthen = Array.isArray(methodAuthenArr) && methodAuthenArr.includes(route.handler);
59
+ const routeMethod = route.method;
60
+ if (classAuthen || methodAuthen) {
61
+ router[routeMethod](route.path, authMiddleware, handler);
62
+ }
63
+ else {
64
+ router[routeMethod](route.path, handler);
65
+ }
66
+ });
67
+ app.use(basePath, router);
68
+ });
69
+ }
@@ -0,0 +1,19 @@
1
+ import { Express } from 'express';
2
+ import { Server as HTTPServer } from 'http';
3
+ import { Server as SocketIOServer } from 'socket.io';
4
+ export interface InitOptions {
5
+ port: number;
6
+ apiControllers?: any[];
7
+ socketServices?: any[];
8
+ authAPIMiddleware?: any;
9
+ onReady?: (app: Express, io: SocketIOServer, httpServer: HTTPServer) => void;
10
+ publicPath?: string;
11
+ expressConfig?: (app: Express) => void;
12
+ socketConfig?: (io: SocketIOServer) => void;
13
+ createRedisAdapter?: () => Promise<any>;
14
+ }
15
+ export declare function initServer(options: InitOptions): Promise<{
16
+ app: import("express-serve-static-core").Express;
17
+ io: SocketIOServer<import("socket.io").DefaultEventsMap, import("socket.io").DefaultEventsMap, import("socket.io").DefaultEventsMap, any>;
18
+ httpServer: HTTPServer;
19
+ }>;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.initServer = initServer;
7
+ const express_1 = __importDefault(require("express"));
8
+ const http_1 = require("http");
9
+ const socket_io_1 = require("socket.io");
10
+ const autoRouter_1 = require("./autoRouter");
11
+ const socketDecorators_1 = require("./socketDecorators");
12
+ async function initServer(options) {
13
+ const app = (0, express_1.default)();
14
+ const httpServer = (0, http_1.createServer)(app);
15
+ const io = new socket_io_1.Server(httpServer, {
16
+ cors: {
17
+ origin: '*',
18
+ methods: ['GET', 'POST']
19
+ },
20
+ transports: ['websocket', 'polling'],
21
+ allowUpgrades: true,
22
+ connectTimeout: 45000,
23
+ pingTimeout: 60000,
24
+ pingInterval: 25000,
25
+ cookie: {
26
+ name: 'io',
27
+ httpOnly: true,
28
+ secure: true,
29
+ sameSite: 'none',
30
+ maxAge: 24 * 60 * 60 * 1000
31
+ },
32
+ connectionStateRecovery: {
33
+ maxDisconnectionDuration: 2 * 60 * 1000,
34
+ skipMiddlewares: true,
35
+ }
36
+ });
37
+ // Redis adapter support
38
+ if (options.createRedisAdapter) {
39
+ try {
40
+ const redisAdapter = await options.createRedisAdapter();
41
+ if (redisAdapter) {
42
+ io.adapter(redisAdapter);
43
+ console.log('✅ Socket.IO Redis adapter configured');
44
+ }
45
+ }
46
+ catch (err) {
47
+ console.warn('⚠️ Redis adapter failed to initialize, using default adapter:', err);
48
+ }
49
+ }
50
+ // Express config
51
+ app.use(express_1.default.json());
52
+ app.use((req, res, next) => {
53
+ res.header('Access-Control-Allow-Origin', '*');
54
+ res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
55
+ res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
56
+ res.header('Access-Control-Allow-Credentials', 'true');
57
+ next();
58
+ });
59
+ if (options.publicPath) {
60
+ app.use(express_1.default.static(options.publicPath));
61
+ }
62
+ if (options.expressConfig) {
63
+ options.expressConfig(app);
64
+ }
65
+ // Register API controllers
66
+ if (options.apiControllers && options.apiControllers.length > 0) {
67
+ (0, autoRouter_1.registerRoutes)(app, options.authAPIMiddleware, ...options.apiControllers);
68
+ }
69
+ // Register socket services
70
+ if (options.socketServices && options.socketServices.length > 0) {
71
+ (0, socketDecorators_1.registerSocketServices)(io, ...options.socketServices);
72
+ }
73
+ if (options.socketConfig) {
74
+ options.socketConfig(io);
75
+ }
76
+ // Error handling
77
+ app.use((err, req, res, next) => {
78
+ console.error('Error:', err);
79
+ res.status(500).json({ error: 'Something went wrong!', details: err.message });
80
+ });
81
+ httpServer.listen(options.port, () => {
82
+ console.log(`Server is running on http://localhost:${options.port}`);
83
+ if (options.onReady)
84
+ options.onReady(app, io, httpServer);
85
+ });
86
+ return { app, io, httpServer };
87
+ }
@@ -0,0 +1,8 @@
1
+ import 'reflect-metadata';
2
+ import { Server } from 'socket.io';
3
+ export declare function AuthenSocket(): MethodDecorator & ClassDecorator;
4
+ export declare function SocketService(): ClassDecorator;
5
+ export declare function OnEvent(event: string): MethodDecorator;
6
+ export declare function OnDisconnect(): MethodDecorator;
7
+ export declare function OnError(): MethodDecorator;
8
+ export declare function registerSocketServices(io: Server, ...serviceClasses: any[]): void;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthenSocket = AuthenSocket;
4
+ exports.SocketService = SocketService;
5
+ exports.OnEvent = OnEvent;
6
+ exports.OnDisconnect = OnDisconnect;
7
+ exports.OnError = OnError;
8
+ exports.registerSocketServices = registerSocketServices;
9
+ require("reflect-metadata");
10
+ const SOCKET_SERVICE_META = Symbol('socket_service');
11
+ const SOCKET_EVENTS_META = Symbol('socket_events');
12
+ const SOCKET_AUTHEN_META = Symbol('socket_authen');
13
+ // Decorator cho xác thực socket
14
+ function AuthenSocket() {
15
+ return (target, propertyKey) => {
16
+ if (propertyKey) {
17
+ // Method
18
+ const authenMethods = Reflect.getMetadata(SOCKET_AUTHEN_META, target.constructor) || [];
19
+ authenMethods.push(propertyKey);
20
+ Reflect.defineMetadata(SOCKET_AUTHEN_META, authenMethods, target.constructor);
21
+ }
22
+ else {
23
+ // Class
24
+ Reflect.defineMetadata(SOCKET_AUTHEN_META, true, target);
25
+ }
26
+ };
27
+ }
28
+ function SocketService() {
29
+ return (target) => {
30
+ Reflect.defineMetadata(SOCKET_SERVICE_META, true, target);
31
+ };
32
+ }
33
+ function OnEvent(event) {
34
+ return (target, propertyKey) => {
35
+ const events = Reflect.getMetadata(SOCKET_EVENTS_META, target.constructor) || [];
36
+ events.push({ type: 'event', event, handler: propertyKey });
37
+ Reflect.defineMetadata(SOCKET_EVENTS_META, events, target.constructor);
38
+ };
39
+ }
40
+ function OnDisconnect() {
41
+ return (target, propertyKey) => {
42
+ const events = Reflect.getMetadata(SOCKET_EVENTS_META, target.constructor) || [];
43
+ events.push({ type: 'disconnect', handler: propertyKey });
44
+ Reflect.defineMetadata(SOCKET_EVENTS_META, events, target.constructor);
45
+ };
46
+ }
47
+ function OnError() {
48
+ return (target, propertyKey) => {
49
+ const events = Reflect.getMetadata(SOCKET_EVENTS_META, target.constructor) || [];
50
+ events.push({ type: 'error', handler: propertyKey });
51
+ Reflect.defineMetadata(SOCKET_EVENTS_META, events, target.constructor);
52
+ };
53
+ }
54
+ function registerSocketServices(io, ...serviceClasses) {
55
+ // Import middleware xác thực socket
56
+ const { authSocketToken } = require('../middleware/auth');
57
+ io.on('connection', (socket) => {
58
+ serviceClasses.forEach(ServiceClass => {
59
+ if (!Reflect.getMetadata(SOCKET_SERVICE_META, ServiceClass))
60
+ return;
61
+ const instance = new ServiceClass();
62
+ const events = Reflect.getMetadata(SOCKET_EVENTS_META, ServiceClass) || [];
63
+ const classAuthen = Reflect.getMetadata(SOCKET_AUTHEN_META, ServiceClass);
64
+ const methodAuthenArr = Reflect.getMetadata(SOCKET_AUTHEN_META, ServiceClass) || [];
65
+ events.forEach((evt) => {
66
+ const methodAuthen = Array.isArray(methodAuthenArr) && methodAuthenArr.includes(evt.handler);
67
+ const needAuth = classAuthen || methodAuthen;
68
+ const handler = (...args) => {
69
+ Promise.resolve(instance[evt.handler](socket, ...args)).catch(console.error);
70
+ };
71
+ if (evt.type === 'event') {
72
+ socket.on(evt.event, (...args) => {
73
+ if (needAuth) {
74
+ authSocketToken(socket, (err) => {
75
+ if (err)
76
+ return socket.emit('server:error', err.message);
77
+ handler(...args);
78
+ });
79
+ }
80
+ else {
81
+ handler(...args);
82
+ }
83
+ });
84
+ }
85
+ else if (evt.type === 'disconnect') {
86
+ socket.on('disconnect', (...args) => handler(...args));
87
+ }
88
+ else if (evt.type === 'error') {
89
+ socket.on('error', (...args) => handler(...args));
90
+ }
91
+ });
92
+ });
93
+ });
94
+ }
@@ -0,0 +1,4 @@
1
+ import { Response, NextFunction } from 'express';
2
+ import { AuthenticatedRequest, AuthenticatedSocket } from '../types';
3
+ export declare const authAPIToken: (req: AuthenticatedRequest, res: Response, next: NextFunction) => void | Response<any, Record<string, any>>;
4
+ export declare const authSocketToken: (socket: AuthenticatedSocket, next: (err?: Error) => void) => void;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authSocketToken = exports.authAPIToken = void 0;
4
+ const utils_1 = require("../utils/utils");
5
+ const authAPIToken = (req, res, next) => {
6
+ // Skip authentication for OPTIONS requests (CORS preflight)
7
+ if (req.method === 'OPTIONS') {
8
+ console.log('🔄 Skipping auth for OPTIONS request (CORS preflight)');
9
+ return next();
10
+ }
11
+ console.log('🔍 Request method:', req.method);
12
+ console.log('🔍 Request URL:', req.url);
13
+ const auth = req.headers.authorization; // "Bearer <token>"
14
+ const token = (auth === null || auth === void 0 ? void 0 : auth.startsWith("Bearer ")) ? auth.slice(7) : undefined;
15
+ console.log('🔑 API authentication token:', token);
16
+ if (!token) {
17
+ return res.status(401).json({
18
+ success: false,
19
+ message: 'No token provided'
20
+ });
21
+ }
22
+ try {
23
+ const decoded = utils_1.utils.tokenDecode(token);
24
+ req.userId = decoded.userId;
25
+ console.log('✅ API authenticated successfully for walletId:', decoded.walletId);
26
+ next();
27
+ }
28
+ catch (error) {
29
+ console.error(error);
30
+ return res.status(403).json({
31
+ success: false,
32
+ message: error
33
+ });
34
+ }
35
+ };
36
+ exports.authAPIToken = authAPIToken;
37
+ const authSocketToken = (socket, next) => {
38
+ console.log('🔐 Socket authentication middleware triggered for socket:', socket.id);
39
+ try {
40
+ var token = socket.handshake.auth.token;
41
+ console.log(socket.handshake);
42
+ console.log(`🔑 Socket authentication token: ${token ? 'Present' : 'Missing'}`);
43
+ // Temporarily allow connections without token for debugging
44
+ if (!token) {
45
+ console.log('⚠️ No token provided, but allowing connection for debugging');
46
+ token = socket.handshake.query.token;
47
+ }
48
+ const decoded = utils_1.utils.tokenDecode(token);
49
+ console.log(decoded);
50
+ socket.userId = decoded.userId;
51
+ next();
52
+ }
53
+ catch (error) {
54
+ console.error('❌ Socket authentication error:', error);
55
+ // Temporarily allow connection even with invalid token for debugging
56
+ console.log('⚠️ Invalid token, but allowing connection for debugging');
57
+ socket.userId = 'anonymous';
58
+ next();
59
+ }
60
+ };
61
+ exports.authSocketToken = authSocketToken;
@@ -0,0 +1,22 @@
1
+ import { Socket } from 'socket.io';
2
+ import { Request } from 'express';
3
+ export interface ApiResponse {
4
+ success: boolean;
5
+ message: string;
6
+ data?: any;
7
+ }
8
+ export interface AuthenticatedSocket extends Socket {
9
+ userId?: string;
10
+ }
11
+ export interface AuthenticatedRequest extends Request {
12
+ userId?: string;
13
+ }
14
+ export interface Vector3 {
15
+ x: number;
16
+ y: number;
17
+ z: number;
18
+ }
19
+ export interface Vector2 {
20
+ x: number;
21
+ y: number;
22
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,45 @@
1
+ import { Vector3 } from ".";
2
+ /**
3
+ * Loại hình dạng của obstacle
4
+ */
5
+ export declare enum ObstacleShape {
6
+ BOX = "box",
7
+ CYLINDER = "cylinder"
8
+ }
9
+ /**
10
+ * Obstacle (vật cản) trên map
11
+ */
12
+ export interface Obstacle {
13
+ id: string;
14
+ shape: ObstacleShape;
15
+ position: Vector3;
16
+ size: Vector3;
17
+ rotation?: Vector3;
18
+ }
19
+ /**
20
+ * Bounding box để kiểm tra collision
21
+ */
22
+ export interface BoundingBox {
23
+ min: Vector3;
24
+ max: Vector3;
25
+ }
26
+ /**
27
+ * Map configuration
28
+ */
29
+ export interface MapData {
30
+ id: string;
31
+ name: string;
32
+ width: number;
33
+ length: number;
34
+ height?: number;
35
+ obstacles: Obstacle[];
36
+ spawnPoints?: Vector3[];
37
+ }
38
+ /**
39
+ * Collision check result
40
+ */
41
+ export interface CollisionResult {
42
+ hasCollision: boolean;
43
+ obstacle?: Obstacle;
44
+ penetrationDepth?: number;
45
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ // Map and Obstacle types
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ObstacleShape = void 0;
5
+ /**
6
+ * Loại hình dạng của obstacle
7
+ */
8
+ var ObstacleShape;
9
+ (function (ObstacleShape) {
10
+ ObstacleShape["BOX"] = "box";
11
+ ObstacleShape["CYLINDER"] = "cylinder";
12
+ })(ObstacleShape || (exports.ObstacleShape = ObstacleShape = {}));
@@ -0,0 +1,35 @@
1
+ import { Obstacle, CollisionResult, MapData } from '../types/map';
2
+ import { Vector3 } from '../types';
3
+ export declare class CollisionDetector {
4
+ private static mapsDirectory;
5
+ static loadMap(nameMap: string): MapData;
6
+ /**
7
+ * Kiểm tra va chạm giữa player và tất cả obstacles trên map
8
+ * @param playerPosition Vị trí player muốn di chuyển đến
9
+ * @param playerRadius Bán kính collision của player (dạng cylinder/capsule)
10
+ * @param playerHeight Chiều cao của player
11
+ * @param obstacles Danh sách obstacles trên map
12
+ * @returns CollisionResult chứa thông tin về va chạm
13
+ */
14
+ static checkCollision(playerPosition: Vector3, playerRadius: number, playerHeight: number, obstacles: Obstacle[]): CollisionResult;
15
+ /**
16
+ * Kiểm tra va chạm giữa player (cylinder) và box obstacle
17
+ */
18
+ private static checkPlayerVsBox;
19
+ /**
20
+ * Kiểm tra va chạm giữa player (cylinder) và cylinder obstacle
21
+ */
22
+ private static checkPlayerVsCylinder;
23
+ /**
24
+ * Tính bounding box của obstacle
25
+ */
26
+ private static getObstacleBoundingBox;
27
+ /**
28
+ * Kiểm tra xem position có nằm trong giới hạn map không
29
+ */
30
+ static isInMapBounds(position: Vector3, mapWidth: number, mapLength: number): boolean;
31
+ /**
32
+ * Clamp position vào trong giới hạn map
33
+ */
34
+ static clampToMapBounds(position: Vector3, mapWidth: number, mapLength: number): Vector3;
35
+ }
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CollisionDetector = void 0;
7
+ /**
8
+ * Collision Detection Utility
9
+ * Xử lý việc kiểm tra va chạm giữa player và obstacles
10
+ */
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const map_1 = require("../types/map");
14
+ class CollisionDetector {
15
+ static loadMap(nameMap) {
16
+ const filePath = path_1.default.join(this.mapsDirectory, `${nameMap}.json`);
17
+ console.log(`[MapLoader] Loading map from: ${filePath}`);
18
+ const jsonData = fs_1.default.readFileSync(filePath, 'utf8');
19
+ const mapData = JSON.parse(jsonData);
20
+ console.log(`[MapLoader] Loaded map: ${mapData.name} (ID: ${mapData.id})`);
21
+ return mapData;
22
+ }
23
+ /**
24
+ * Kiểm tra va chạm giữa player và tất cả obstacles trên map
25
+ * @param playerPosition Vị trí player muốn di chuyển đến
26
+ * @param playerRadius Bán kính collision của player (dạng cylinder/capsule)
27
+ * @param playerHeight Chiều cao của player
28
+ * @param obstacles Danh sách obstacles trên map
29
+ * @returns CollisionResult chứa thông tin về va chạm
30
+ */
31
+ static checkCollision(playerPosition, playerRadius, playerHeight, obstacles) {
32
+ for (const obstacle of obstacles) {
33
+ let hasCollision = false;
34
+ if (obstacle.shape === map_1.ObstacleShape.BOX) {
35
+ hasCollision = this.checkPlayerVsBox(playerPosition, playerRadius, playerHeight, obstacle);
36
+ }
37
+ else if (obstacle.shape === map_1.ObstacleShape.CYLINDER) {
38
+ hasCollision = this.checkPlayerVsCylinder(playerPosition, playerRadius, playerHeight, obstacle);
39
+ }
40
+ if (hasCollision) {
41
+ return {
42
+ hasCollision: true,
43
+ obstacle: obstacle
44
+ };
45
+ }
46
+ }
47
+ return { hasCollision: false };
48
+ }
49
+ /**
50
+ * Kiểm tra va chạm giữa player (cylinder) và box obstacle
51
+ */
52
+ static checkPlayerVsBox(playerPos, playerRadius, playerHeight, obstacle) {
53
+ // Tính bounding box của obstacle
54
+ const obstacleBox = this.getObstacleBoundingBox(obstacle);
55
+ // Kiểm tra overlap trên trục Y (chiều cao)
56
+ const playerBottom = playerPos.y;
57
+ const playerTop = playerPos.y + playerHeight;
58
+ if (playerTop < obstacleBox.min.y || playerBottom > obstacleBox.max.y) {
59
+ return false; // Không va chạm theo chiều cao
60
+ }
61
+ // Kiểm tra va chạm 2D trên mặt phẳng XZ
62
+ // Tìm điểm gần nhất trên box với player position
63
+ const closestX = Math.max(obstacleBox.min.x, Math.min(playerPos.x, obstacleBox.max.x));
64
+ const closestZ = Math.max(obstacleBox.min.z, Math.min(playerPos.z, obstacleBox.max.z));
65
+ // Tính khoảng cách từ player đến điểm gần nhất
66
+ const distanceX = playerPos.x - closestX;
67
+ const distanceZ = playerPos.z - closestZ;
68
+ const distanceSquared = distanceX * distanceX + distanceZ * distanceZ;
69
+ // Va chạm nếu khoảng cách nhỏ hơn bán kính player
70
+ return distanceSquared < (playerRadius * playerRadius);
71
+ }
72
+ /**
73
+ * Kiểm tra va chạm giữa player (cylinder) và cylinder obstacle
74
+ */
75
+ static checkPlayerVsCylinder(playerPos, playerRadius, playerHeight, obstacle) {
76
+ const obstacleRadius = obstacle.size.x; // Bán kính cylinder là size.x
77
+ const obstacleHeight = obstacle.size.y;
78
+ // Kiểm tra overlap trên trục Y
79
+ const playerBottom = playerPos.y;
80
+ const playerTop = playerPos.y + playerHeight;
81
+ const obstacleBottom = obstacle.position.y - obstacleHeight / 2;
82
+ const obstacleTop = obstacle.position.y + obstacleHeight / 2;
83
+ if (playerTop < obstacleBottom || playerBottom > obstacleTop) {
84
+ return false; // Không va chạm theo chiều cao
85
+ }
86
+ // Kiểm tra va chạm 2D giữa 2 circles trên mặt phẳng XZ
87
+ const distanceX = playerPos.x - obstacle.position.x;
88
+ const distanceZ = playerPos.z - obstacle.position.z;
89
+ const distanceSquared = distanceX * distanceX + distanceZ * distanceZ;
90
+ const minDistance = playerRadius + obstacleRadius;
91
+ return distanceSquared < (minDistance * minDistance);
92
+ }
93
+ /**
94
+ * Tính bounding box của obstacle
95
+ */
96
+ static getObstacleBoundingBox(obstacle) {
97
+ const halfSize = {
98
+ x: obstacle.size.x / 2,
99
+ y: obstacle.size.y / 2,
100
+ z: obstacle.size.z / 2
101
+ };
102
+ return {
103
+ min: {
104
+ x: obstacle.position.x - halfSize.x,
105
+ y: obstacle.position.y - halfSize.y,
106
+ z: obstacle.position.z - halfSize.z
107
+ },
108
+ max: {
109
+ x: obstacle.position.x + halfSize.x,
110
+ y: obstacle.position.y + halfSize.y,
111
+ z: obstacle.position.z + halfSize.z
112
+ }
113
+ };
114
+ }
115
+ /**
116
+ * Kiểm tra xem position có nằm trong giới hạn map không
117
+ */
118
+ static isInMapBounds(position, mapWidth, mapLength) {
119
+ const halfWidth = mapWidth / 2;
120
+ const halfLength = mapLength / 2;
121
+ return position.x >= -halfWidth && position.x <= halfWidth &&
122
+ position.z >= -halfLength && position.z <= halfLength;
123
+ }
124
+ /**
125
+ * Clamp position vào trong giới hạn map
126
+ */
127
+ static clampToMapBounds(position, mapWidth, mapLength) {
128
+ const halfWidth = mapWidth / 2;
129
+ const halfLength = mapLength / 2;
130
+ return {
131
+ x: Math.max(-halfWidth, Math.min(halfWidth, position.x)),
132
+ y: position.y,
133
+ z: Math.max(-halfLength, Math.min(halfLength, position.z))
134
+ };
135
+ }
136
+ }
137
+ exports.CollisionDetector = CollisionDetector;
138
+ CollisionDetector.mapsDirectory = path_1.default.join(__dirname, '../../maps');
@@ -0,0 +1 @@
1
+ export declare function sendDiscordNotification(message: string): Promise<void>;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.sendDiscordNotification = sendDiscordNotification;
7
+ const discord_js_1 = require("discord.js");
8
+ const dotenv_1 = __importDefault(require("dotenv"));
9
+ dotenv_1.default.config();
10
+ const token = process.env.DISCORD_TOKEN;
11
+ const channelId = process.env.CHANNEL_ID;
12
+ if (!token || !channelId) {
13
+ console.error("Vui lòng cung cấp DISCORD_TOKEN và CHANNEL_ID trong .env");
14
+ process.exit(1);
15
+ }
16
+ const client = new discord_js_1.Client({
17
+ intents: [discord_js_1.GatewayIntentBits.Guilds, discord_js_1.GatewayIntentBits.GuildMessages],
18
+ });
19
+ client.once(discord_js_1.Events.ClientReady, async () => {
20
+ var _a;
21
+ console.log(`Bot đã đăng nhập với tên: ${(_a = client.user) === null || _a === void 0 ? void 0 : _a.tag}`);
22
+ const channel = client.channels.cache.get(channelId);
23
+ if (!channel) {
24
+ console.error("Không tìm thấy kênh!");
25
+ return;
26
+ }
27
+ // channel.send("Bot đã hoạt động! 🚀");
28
+ });
29
+ client.on(discord_js_1.Events.MessageCreate, async (message) => {
30
+ // Bỏ qua tin nhắn của chính bot
31
+ if (message.author.bot)
32
+ return;
33
+ // Kiểm tra bot có bị tag hay không
34
+ if (message.mentions.has(client.user)) {
35
+ console.log(`📩 Được tag bởi ${message.author.tag}: ${message.content}`);
36
+ if (message.content.toLowerCase().includes("game"))
37
+ await message.reply(`Chào ${message.author.username}! Game đây nhá!!\n
38
+ 🤖 [Shadow Javelin](https://t.me/gamedevtoi_bot/shadow_javelin)\n
39
+ 🍉 [Garden DHH](https://t.me/gamedevtoi_bot/gardenDHH)`);
40
+ }
41
+ });
42
+ // Xử lý lỗi Discord client
43
+ client.on(discord_js_1.Events.Error, (error) => {
44
+ console.error('Discord client error:', error);
45
+ });
46
+ // Đăng nhập với xử lý lỗi
47
+ client.login(token).catch((error) => {
48
+ console.error('❌ Discord login failed:', error.message);
49
+ console.log('⚠️ Discord bot không khả dụng');
50
+ });
51
+ function Connect() {
52
+ return new Promise((resolve, reject) => {
53
+ // Nếu đã ready thì resolve ngay
54
+ if (client.isReady()) {
55
+ resolve(client);
56
+ return;
57
+ }
58
+ // Timeout sau 5 giây nếu không kết nối được
59
+ const timeout = setTimeout(() => {
60
+ clearInterval(check);
61
+ reject(new Error('Discord connection timeout'));
62
+ }, 5000);
63
+ const check = setInterval(() => {
64
+ if (client.isReady()) {
65
+ clearTimeout(timeout);
66
+ clearInterval(check);
67
+ resolve(client);
68
+ }
69
+ }, 100);
70
+ });
71
+ }
72
+ // Hàm gửi thông báo
73
+ async function sendDiscordNotification(message) {
74
+ try {
75
+ await Connect();
76
+ console.log(`Gửi tin đến server!`);
77
+ const channel = client.channels.cache.get(channelId);
78
+ if (channel) {
79
+ await channel.send(message);
80
+ }
81
+ else {
82
+ console.error("Không thể gửi tin nhắn, kênh không tồn tại!");
83
+ }
84
+ }
85
+ catch (error) {
86
+ console.error("❌ Không thể gửi Discord notification:", error instanceof Error ? error.message : error);
87
+ // Server vẫn tiếp tục chạy, chỉ log lỗi
88
+ }
89
+ }
@@ -0,0 +1,4 @@
1
+ import TelegramBot from "node-telegram-bot-api";
2
+ export declare const botTele: TelegramBot;
3
+ export declare function sendMessage(message: string, userId?: string, thread_id?: string): Promise<void>;
4
+ export declare function sendMessageNoti(message: string): Promise<void>;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.botTele = void 0;
7
+ exports.sendMessage = sendMessage;
8
+ exports.sendMessageNoti = sendMessageNoti;
9
+ const node_telegram_bot_api_1 = __importDefault(require("node-telegram-bot-api"));
10
+ const dotenv_1 = __importDefault(require("dotenv"));
11
+ const node_fetch_1 = __importDefault(require("node-fetch"));
12
+ dotenv_1.default.config();
13
+ const token = process.env.TELEGRAM_BOT_TOKEN;
14
+ const gameUrl = "https://t.me/gamedevtoi_bot/shadow_javelin";
15
+ if (!token) {
16
+ throw new Error("Missing TELEGRAM_BOT_TOKEN in .env file");
17
+ }
18
+ exports.botTele = new node_telegram_bot_api_1.default(token, { polling: true });
19
+ exports.botTele.on("inline_query", async (query) => {
20
+ const results = [
21
+ {
22
+ type: "article",
23
+ id: "1",
24
+ title: "PlayGame",
25
+ input_message_content: {
26
+ message_text: gameUrl,
27
+ },
28
+ }
29
+ ];
30
+ exports.botTele.answerInlineQuery(query.id, results);
31
+ });
32
+ exports.botTele.onText(/\/games/, (msg) => {
33
+ exports.botTele.sendMessage(msg.chat.id, "Chào bạn! Đây là những game hiện có 🎮", {
34
+ reply_markup: {
35
+ keyboard: [[{ text: "🎮 Shadow Javelin" }, { text: "🍉 Garden DHH" }]],
36
+ resize_keyboard: true,
37
+ one_time_keyboard: false,
38
+ },
39
+ });
40
+ });
41
+ exports.botTele.on("message", (msg) => {
42
+ var _a;
43
+ if (msg.text === "🎮 Shadow Javelin") {
44
+ exports.botTele.sendMessage(msg.chat.id, `🚀 Nhấn vào đây để chơi: [Shadow Javelin](${gameUrl})`, {
45
+ parse_mode: "Markdown",
46
+ });
47
+ }
48
+ if (msg.text === "🍉 Garden DHH") {
49
+ exports.botTele.sendMessage(msg.chat.id, `🚀 Nhấn vào đây để chơi: [Garden DHH](https://t.me/gamedevtoi_bot/gardenDHH)`, {
50
+ parse_mode: "Markdown",
51
+ });
52
+ }
53
+ if ((_a = msg.text) === null || _a === void 0 ? void 0 : _a.startsWith("/noti")) {
54
+ //sendNotiAllUser(msg.text.replace("/noti", ""));
55
+ }
56
+ });
57
+ async function sendMessage(message, userId = "", thread_id = "") {
58
+ try {
59
+ const url = `https://api.telegram.org/bot${token}/sendMessage`;
60
+ const response = await (0, node_fetch_1.default)(url, {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: JSON.stringify({
64
+ chat_id: userId,
65
+ message_thread_id: thread_id,
66
+ text: message,
67
+ }),
68
+ });
69
+ const data = await response.json();
70
+ }
71
+ catch (e) {
72
+ console.error(`Send message to tele fail!`);
73
+ }
74
+ }
75
+ async function sendMessageNoti(message) {
76
+ sendMessage(message, process.env.TELEGRAM_CHATID || "", process.env.TELEGRAM_THREAD_ID || "");
77
+ }
78
+ console.log("🤖 Bot is running...");
@@ -0,0 +1,5 @@
1
+ export declare const utils: {
2
+ getMinutesPassed: (startAt: string) => number;
3
+ tokenDecode(token: string): any;
4
+ tokenEncode(data: any): string;
5
+ };
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.utils = void 0;
7
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
8
+ const dotenv_1 = __importDefault(require("dotenv"));
9
+ dotenv_1.default.config();
10
+ exports.utils = {
11
+ getMinutesPassed: function (startAt) {
12
+ const startDate = new Date(startAt);
13
+ const currentDate = new Date();
14
+ const diffInMilliseconds = currentDate.getTime() - startDate.getTime();
15
+ return Math.floor(diffInMilliseconds / (1000 * 60)); // Chuyển đổi từ milliseconds sang phút
16
+ },
17
+ tokenDecode(token) {
18
+ const secret = process.env.JWT_SECRET || 'your-secret-key';
19
+ console.log('🔍 JWT Secret status:', process.env.JWT_SECRET ? 'Using environment JWT_SECRET' : 'Using default secret');
20
+ try {
21
+ const decoded = jsonwebtoken_1.default.verify(token, secret);
22
+ return decoded;
23
+ }
24
+ catch (error) {
25
+ console.error('❌ JWT verification failed:', error);
26
+ console.error('🔍 Token preview:', token.substring(0, 50) + '...');
27
+ console.error('🔍 Secret being used:', secret.substring(0, 10) + '...');
28
+ throw error;
29
+ }
30
+ },
31
+ tokenEncode(data) {
32
+ const secret = process.env.JWT_SECRET || 'your-secret-key';
33
+ console.log('🔍 Creating JWT with secret:', process.env.JWT_SECRET ? 'Using environment JWT_SECRET' : 'Using default secret');
34
+ const token = jsonwebtoken_1.default.sign(data, secret, { expiresIn: '7d' });
35
+ console.log('✅ JWT token created successfully');
36
+ console.log('🔍 Token:', token);
37
+ return token;
38
+ }
39
+ };
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "ts-game-decorators",
3
+ "version": "1.0.0",
4
+ "description": "Express & Socket.IO decorators for auto routing and event handling. using for backend game development.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc -p tsconfig.json",
12
+ "prepare": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "express",
16
+ "socket.io",
17
+ "decorators",
18
+ "typescript",
19
+ "router",
20
+ "auto-router"
21
+ ],
22
+ "author": "gamedevtoi",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "express": "^4.18.2",
26
+ "socket.io": "^4.7.4",
27
+ "reflect-metadata": "^0.1.13"
28
+ },
29
+ "devDependencies": {
30
+ "typescript": "^5.0.0"
31
+ }
32
+ }