This repository was archived by the owner on Jul 9, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathdatabase.js
More file actions
200 lines (184 loc) · 5.99 KB
/
database.js
File metadata and controls
200 lines (184 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
const mysql = require('mysql2/promise');
require('dotenv').config();
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: process.env.DB_PORT || 3306,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
connectTimeout: 60000,
enableKeepAlive: true,
keepAliveInitialDelay: 10000
});
async function initDatabase() {
const connection = await pool.getConnection();
try {
// Create registered table
await connection.query(`
CREATE TABLE IF NOT EXISTS registered (
id INT AUTO_INCREMENT PRIMARY KEY,
mc_user VARCHAR(255) NOT NULL,
discord_user VARCHAR(255) NOT NULL,
discord_id VARCHAR(255) NOT NULL UNIQUE
)
`);
// Create stats table
await connection.query(`
CREATE TABLE IF NOT EXISTS stats (
id INT AUTO_INCREMENT PRIMARY KEY,
discord_id VARCHAR(255) NOT NULL UNIQUE,
elo INT DEFAULT 0,
wins INT DEFAULT 0,
lost INT DEFAULT 0,
wlr FLOAT DEFAULT 0,
rank VARCHAR(255),
games INT DEFAULT 0,
mvp INT DEFAULT 0,
bed_breaker INT DEFAULT 0
)
`);
// Create others table
await connection.query(`
CREATE TABLE IF NOT EXISTS others (
id INT AUTO_INCREMENT PRIMARY KEY,
guild_id VARCHAR(255) NOT NULL UNIQUE,
category_id VARCHAR(255),
channel_4v4 VARCHAR(255),
channel_3v3 VARCHAR(255),
channel_2v2 VARCHAR(255)
)
`);
// Create games table
await connection.query(`
CREATE TABLE IF NOT EXISTS games (
id INT AUTO_INCREMENT PRIMARY KEY,
game_number VARCHAR(255) NOT NULL UNIQUE,
gamemode VARCHAR(10) NOT NULL,
status ENUM('queued', 'in_progress', 'submitted', 'validated', 'voided') DEFAULT 'queued',
team1_members JSON,
team2_members JSON,
winning_team VARCHAR(10),
mvp VARCHAR(255),
proof_image VARCHAR(255),
bed_breaker TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
category_id VARCHAR(255),
voice_channel_id VARCHAR(255),
text_channel_id VARCHAR(255)
)
`);
// Create punishments table
await connection.query(`
CREATE TABLE IF NOT EXISTS punishments (
id INT AUTO_INCREMENT PRIMARY KEY,
discord_id VARCHAR(255) NOT NULL,
type ENUM('strike', 'ban') NOT NULL,
amount INT DEFAULT 0,
expiration TIMESTAMP NULL,
reason TEXT,
issued_by VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
// Create rank_roles table
await connection.query(`
CREATE TABLE IF NOT EXISTS rank_roles (
guild_id VARCHAR(20) NOT NULL,
rank_name VARCHAR(50) NOT NULL,
role_id VARCHAR(20) NOT NULL,
display_name VARCHAR(50) NOT NULL,
position INT NOT NULL,
PRIMARY KEY (guild_id, rank_name)
)
`);
// Add indices for rank_roles
await connection.query(`
CREATE INDEX IF NOT EXISTS idx_guild_rank ON rank_roles(guild_id, rank_name);
`);
await connection.query(`
CREATE INDEX IF NOT EXISTS idx_role_id ON rank_roles(role_id);
`);
console.log('Database initialized');
} catch (error) {
console.error('Error initializing database:', error);
} finally {
connection.release();
}
}
async function healthCheck() {
try {
const connection = await pool.getConnection();
await connection.query('SELECT 1');
connection.release();
} catch (error) {
console.error('Health check failed:', error);
}
}
async function query(table, operation, ...args) {
let retries = 3;
while (retries > 0) {
try {
const connection = await pool.getConnection();
try {
switch (operation) {
case 'find':
const [rows] = await connection.query(`SELECT * FROM ${table} WHERE ?`, args[0]);
return rows;
case 'findOne':
const [row] = await connection.query(`SELECT * FROM ${table} WHERE ? LIMIT 1`, args[0]);
return row[0];
case 'updateOne':
const [updateResult] = await connection.query(`UPDATE ${table} SET ? WHERE ?`, [args[1].$set, args[0]]);
return { modifiedCount: updateResult.affectedRows };
case 'raw':
const [rawRows] = await connection.query(...args);
return rawRows;
case 'insertOne':
const [insertResult] = await connection.query(`INSERT INTO ${table} SET ?`, args[0]);
return { insertId: insertResult.insertId };
case 'deleteOne':
case 'deleteMany':
const [deleteResult] = await connection.query(`DELETE FROM ${table} WHERE ?`, args[0]);
return { deletedCount: deleteResult.affectedRows };
case 'select':
const [selectRows] = await connection.query(args[0]);
return selectRows;
default:
throw new Error(`Unsupported operation: ${operation}`);
}
} finally {
connection.release();
}
} catch (error) {
console.error(`Database query error (attempts left: ${retries}):`, error);
if (error.code === 'ECONNRESET' || error.code === 'PROTOCOL_CONNECTION_LOST') {
retries--;
await new Promise(resolve => setTimeout(resolve, 1000));
continue;
}
throw error;
}
}
throw new Error('Max retries reached. Unable to connect to database.');
}
// Health check interval
setInterval(healthCheck, 30000);
// Initialize database on startup
initDatabase();
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('Shutting down gracefully...');
try {
await pool.end();
console.log('All database connections closed.');
process.exit(0);
} catch (err) {
console.error('Error during shutdown:', err);
process.exit(1);
}
});
module.exports = { query };