@onurege3467/zerohelper 7.1.0 → 8.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.
package/database/index.js CHANGED
@@ -1,10 +1,13 @@
1
+ // database-factory.js
1
2
  const IDatabase = require('./IDatabase');
2
3
  const MySQLDatabase = require('./mysql');
3
4
  const SQLiteDatabase = require('./sqlite');
4
5
  const MongoDBDatabase = require('./mongodb');
5
6
  const JsonDatabase = require('./json');
6
7
  const PostgreSQLDatabase = require('./pg');
8
+ const RedisDatabase = require('./redis'); // Yeni Redis adapter
7
9
  const CacheWrapper = require('./cacheWrapper');
10
+ const MigrationManager = require('./migration');
8
11
 
9
12
  const adapters = {
10
13
  mysql: MySQLDatabase,
@@ -12,6 +15,7 @@ const adapters = {
12
15
  mongodb: MongoDBDatabase,
13
16
  postgres: PostgreSQLDatabase,
14
17
  json: JsonDatabase,
18
+ redis: RedisDatabase, // Redis adapter eklendi
15
19
  };
16
20
 
17
21
  /**
@@ -19,7 +23,7 @@ const adapters = {
19
23
  * Bu bir "Fabrika Fonksiyonu"dur.
20
24
  *
21
25
  * @param {object} options - Yapılandırma seçenekleri.
22
- * @param {keyof adapters} options.adapter - Kullanılacak adaptör ('mysql', 'sqlite', vb.).
26
+ * @param {keyof adapters} options.adapter - Kullanılacak adaptör ('mysql', 'sqlite', 'redis', vb.).
23
27
  * @param {object} options.config - Seçilen adaptöre özel yapılandırma.
24
28
  * @returns {IDatabase} - IDatabase arayüzünü uygulayan bir örnek döndürür.
25
29
  */
@@ -61,5 +65,8 @@ function createDatabase(options) {
61
65
  // Artık varsayılan olarak bu fonksiyonu export ediyoruz.
62
66
  module.exports = createDatabase;
63
67
 
68
+ // Migration manager'ı da export ediyoruz
69
+ module.exports.MigrationManager = MigrationManager;
70
+
64
71
  // Eğer hem fonksiyonu hem de tipleri export etmek isterseniz:
65
- // module.exports = { createDatabase };
72
+ // module.exports = { createDatabase, MigrationManager };
package/database/json.js CHANGED
@@ -106,6 +106,11 @@ class JsonDatabase extends IDatabase{
106
106
  return Object.keys(where).every(key => row[key] === where[key]);
107
107
  }
108
108
 
109
+ // Alias for consistency with cache wrapper
110
+ _matchesWhere(row, where) {
111
+ return this._matches(row, where);
112
+ }
113
+
109
114
  // --- GENEL (PUBLIC) API ---
110
115
 
111
116
  async ensureTable(table) {
@@ -214,6 +219,60 @@ class JsonDatabase extends IDatabase{
214
219
  });
215
220
  }
216
221
 
222
+ /**
223
+ * Numerik alanları artırır (increment).
224
+ * @param {string} table - Verinin güncelleneceği tablo adı.
225
+ * @param {object} increments - Artırılacak alanlar ve miktarları.
226
+ * @param {object} where - Güncelleme koşulları.
227
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
228
+ */
229
+ async increment(table, increments, where = {}) {
230
+ await this.ensureTable(table);
231
+ return this._queueRequest(() => {
232
+ let affectedCount = 0;
233
+ this.db[table].forEach(row => {
234
+ if (this._matchesWhere(row, where)) {
235
+ for (const [field, value] of Object.entries(increments)) {
236
+ if (typeof row[field] === 'number') {
237
+ row[field] += value;
238
+ } else {
239
+ row[field] = value; // Eğer field sayı değilse, yeni değeri ata
240
+ }
241
+ }
242
+ affectedCount++;
243
+ }
244
+ });
245
+ return affectedCount;
246
+ });
247
+ }
248
+
249
+ /**
250
+ * Numerik alanları azaltır (decrement).
251
+ * @param {string} table - Verinin güncelleneceği tablo adı.
252
+ * @param {object} decrements - Azaltılacak alanlar ve miktarları.
253
+ * @param {object} where - Güncelleme koşulları.
254
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
255
+ */
256
+ async decrement(table, decrements, where = {}) {
257
+ await this.ensureTable(table);
258
+ return this._queueRequest(() => {
259
+ let affectedCount = 0;
260
+ this.db[table].forEach(row => {
261
+ if (this._matchesWhere(row, where)) {
262
+ for (const [field, value] of Object.entries(decrements)) {
263
+ if (typeof row[field] === 'number') {
264
+ row[field] -= value;
265
+ } else {
266
+ row[field] = -value; // Eğer field sayı değilse, negatif değeri ata
267
+ }
268
+ }
269
+ affectedCount++;
270
+ }
271
+ });
272
+ return affectedCount;
273
+ });
274
+ }
275
+
217
276
  async close() {
218
277
  await this._saveNow();
219
278
  }
@@ -0,0 +1,227 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ class MigrationManager {
5
+ constructor(database, options = {}) {
6
+ this.db = database;
7
+ this.migrationsDir = options.migrationsDir || './migrations';
8
+ this.migrationsTable = options.migrationsTable || 'migrations';
9
+ this.ensureMigrationsDir();
10
+ }
11
+
12
+ ensureMigrationsDir() {
13
+ if (!fs.existsSync(this.migrationsDir)) {
14
+ fs.mkdirSync(this.migrationsDir, { recursive: true });
15
+ }
16
+ }
17
+
18
+ async ensureMigrationsTable() {
19
+ try {
20
+ // Migrations tablosu var mı kontrol et
21
+ await this.db.selectOne(this.migrationsTable, {});
22
+ } catch (error) {
23
+ // Tablo yoksa oluştur
24
+ const createTableSQL = `
25
+ CREATE TABLE IF NOT EXISTS ${this.migrationsTable} (
26
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
27
+ name VARCHAR(255) NOT NULL UNIQUE,
28
+ executed_at DATETIME DEFAULT CURRENT_TIMESTAMP
29
+ )
30
+ `;
31
+
32
+ // SQLite, MySQL, PostgreSQL için farklı SQL'ler
33
+ if (this.db.constructor.name === 'MySQLDatabase') {
34
+ await this.db.query(createTableSQL.replace('AUTOINCREMENT', 'AUTO_INCREMENT'));
35
+ } else if (this.db.constructor.name === 'PostgreSQLDatabase') {
36
+ const pgSQL = `
37
+ CREATE TABLE IF NOT EXISTS ${this.migrationsTable} (
38
+ id SERIAL PRIMARY KEY,
39
+ name VARCHAR(255) NOT NULL UNIQUE,
40
+ executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
41
+ )
42
+ `;
43
+ await this.db.query(pgSQL);
44
+ } else {
45
+ // SQLite
46
+ await this.db.query(createTableSQL);
47
+ }
48
+ }
49
+ }
50
+
51
+ createMigration(name, description = '') {
52
+ const timestamp = Date.now();
53
+ const filename = `${timestamp}_${name}.js`;
54
+ const filePath = path.join(this.migrationsDir, filename);
55
+
56
+ const template = `// Migration: ${name}
57
+ // Description: ${description}
58
+ // Created: ${new Date().toISOString()}
59
+
60
+ module.exports = {
61
+ async up(db) {
62
+ // Migration işlemlerini buraya yazın
63
+ // Örnek:
64
+ // await db.query(\`
65
+ // CREATE TABLE users (
66
+ // id INTEGER PRIMARY KEY AUTOINCREMENT,
67
+ // name VARCHAR(255) NOT NULL,
68
+ // email VARCHAR(255) UNIQUE NOT NULL
69
+ // )
70
+ // \`);
71
+
72
+ console.log('Migration ${name} executed (up)');
73
+ },
74
+
75
+ async down(db) {
76
+ // Rollback işlemlerini buraya yazın
77
+ // Örnek:
78
+ // await db.query('DROP TABLE IF EXISTS users');
79
+
80
+ console.log('Migration ${name} rolled back (down)');
81
+ }
82
+ };
83
+ `;
84
+
85
+ fs.writeFileSync(filePath, template);
86
+ console.log(`Migration oluşturuldu: ${filename}`);
87
+ return filename;
88
+ }
89
+
90
+ getMigrationFiles() {
91
+ const files = fs.readdirSync(this.migrationsDir)
92
+ .filter(file => file.endsWith('.js'))
93
+ .sort();
94
+
95
+ return files.map(file => ({
96
+ name: file,
97
+ path: path.join(this.migrationsDir, file)
98
+ }));
99
+ }
100
+
101
+ async getExecutedMigrations() {
102
+ await this.ensureMigrationsTable();
103
+
104
+ try {
105
+ const executed = await this.db.select(this.migrationsTable, {});
106
+ return executed.map(row => row.name);
107
+ } catch (error) {
108
+ console.error('Executed migrations alınırken hata:', error);
109
+ return [];
110
+ }
111
+ }
112
+
113
+ async getPendingMigrations() {
114
+ const allMigrations = this.getMigrationFiles();
115
+ const executedMigrations = await this.getExecutedMigrations();
116
+
117
+ return allMigrations.filter(migration =>
118
+ !executedMigrations.includes(migration.name)
119
+ );
120
+ }
121
+
122
+ async runMigration(migrationFile, direction = 'up') {
123
+ try {
124
+ delete require.cache[require.resolve(migrationFile.path)];
125
+ const migration = require(migrationFile.path);
126
+
127
+ if (typeof migration[direction] !== 'function') {
128
+ throw new Error(`Migration ${migrationFile.name} has no ${direction} method`);
129
+ }
130
+
131
+ await migration[direction](this.db);
132
+
133
+ if (direction === 'up') {
134
+ await this.db.insert(this.migrationsTable, {
135
+ name: migrationFile.name
136
+ });
137
+ } else if (direction === 'down') {
138
+ await this.db.delete(this.migrationsTable, {
139
+ name: migrationFile.name
140
+ });
141
+ }
142
+
143
+ console.log(`✅ Migration ${migrationFile.name} (${direction}) başarılı`);
144
+ return true;
145
+ } catch (error) {
146
+ console.error(`❌ Migration ${migrationFile.name} (${direction}) hatası:`, error);
147
+ throw error;
148
+ }
149
+ }
150
+
151
+ async migrate() {
152
+ const pending = await this.getPendingMigrations();
153
+
154
+ if (pending.length === 0) {
155
+ console.log('✅ Tüm migration\'lar güncel');
156
+ return;
157
+ }
158
+
159
+ console.log(`🔄 ${pending.length} migration çalıştırılacak...`);
160
+
161
+ for (const migration of pending) {
162
+ await this.runMigration(migration, 'up');
163
+ }
164
+
165
+ console.log('✅ Tüm migration\'lar tamamlandı');
166
+ }
167
+
168
+ async rollback(steps = 1) {
169
+ const executed = await this.getExecutedMigrations();
170
+
171
+ if (executed.length === 0) {
172
+ console.log('❌ Rollback yapılacak migration yok');
173
+ return;
174
+ }
175
+
176
+ const allMigrations = this.getMigrationFiles();
177
+ const toRollback = executed
178
+ .slice(-steps)
179
+ .reverse()
180
+ .map(name => allMigrations.find(m => m.name === name))
181
+ .filter(Boolean);
182
+
183
+ console.log(`🔄 ${toRollback.length} migration rollback edilecek...`);
184
+
185
+ for (const migration of toRollback) {
186
+ await this.runMigration(migration, 'down');
187
+ }
188
+
189
+ console.log('✅ Rollback tamamlandı');
190
+ }
191
+
192
+ async status() {
193
+ const allMigrations = this.getMigrationFiles();
194
+ const executedMigrations = await this.getExecutedMigrations();
195
+
196
+ console.log('\n📋 Migration Durumu:');
197
+ console.log('==================');
198
+
199
+ if (allMigrations.length === 0) {
200
+ console.log('❌ Migration dosyası bulunamadı');
201
+ return;
202
+ }
203
+
204
+ allMigrations.forEach(migration => {
205
+ const status = executedMigrations.includes(migration.name) ? '✅ Executed' : '⏳ Pending';
206
+ console.log(`${status} ${migration.name}`);
207
+ });
208
+
209
+ const pendingCount = allMigrations.length - executedMigrations.length;
210
+ console.log(`\n📊 Toplam: ${allMigrations.length}, Executed: ${executedMigrations.length}, Pending: ${pendingCount}`);
211
+ }
212
+
213
+ async reset() {
214
+ const executed = await this.getExecutedMigrations();
215
+
216
+ if (executed.length === 0) {
217
+ console.log('❌ Reset edilecek migration yok');
218
+ return;
219
+ }
220
+
221
+ console.log(`🔄 ${executed.length} migration reset edilecek...`);
222
+ await this.rollback(executed.length);
223
+ console.log('✅ Tüm migration\'lar reset edildi');
224
+ }
225
+ }
226
+
227
+ module.exports = MigrationManager;
@@ -150,6 +150,51 @@ class MongoDBDatabase extends IDatabase{
150
150
  });
151
151
  }
152
152
 
153
+ /**
154
+ * Numerik alanları artırır (increment).
155
+ * @param {string} collection - Verinin güncelleneceği koleksiyon adı.
156
+ * @param {object} increments - Artırılacak alanlar ve miktarları.
157
+ * @param {object} where - Güncelleme koşulları.
158
+ * @returns {Promise<number>} Etkilenen belge sayısı.
159
+ */
160
+ async increment(collection, increments, where = {}) {
161
+ return this._queueRequest(async () => {
162
+ await this.ensureCollection(collection);
163
+ const db = await this._connectionPromise;
164
+ const incObject = {};
165
+
166
+ for (const [field, value] of Object.entries(increments)) {
167
+ incObject[field] = value;
168
+ }
169
+
170
+ const result = await db.collection(collection).updateMany(where, { $inc: incObject });
171
+ return result.modifiedCount;
172
+ });
173
+ }
174
+
175
+ /**
176
+ * Numerik alanları azaltır (decrement).
177
+ * @param {string} collection - Verinin güncelleneceği koleksiyon adı.
178
+ * @param {object} decrements - Azaltılacak alanlar ve miktarları.
179
+ * @param {object} where - Güncelleme koşulları.
180
+ * @returns {Promise<number>} Etkilenen belge sayısı.
181
+ */
182
+ async decrement(collection, decrements, where = {}) {
183
+ return this._queueRequest(async () => {
184
+ await this.ensureCollection(collection);
185
+ const db = await this._connectionPromise;
186
+ const incObject = {};
187
+
188
+ // Decrement için negatif değerler kullanıyoruz
189
+ for (const [field, value] of Object.entries(decrements)) {
190
+ incObject[field] = -value;
191
+ }
192
+
193
+ const result = await db.collection(collection).updateMany(where, { $inc: incObject });
194
+ return result.modifiedCount;
195
+ });
196
+ }
197
+
153
198
  async close() {
154
199
  await this.client.close();
155
200
  }
package/database/mysql.js CHANGED
@@ -460,6 +460,67 @@ class MySQLDatabase extends IDatabase{
460
460
  }
461
461
  return value;
462
462
  }
463
+
464
+ /**
465
+ * WHERE clause oluşturur
466
+ * @param {object} where - WHERE koşulları
467
+ * @returns {object} - whereClause string ve values array
468
+ */
469
+ buildWhereClause(where = {}) {
470
+ const conditions = Object.keys(where);
471
+ if (conditions.length === 0) {
472
+ return { whereClause: '', values: [] };
473
+ }
474
+
475
+ const whereClause = ' WHERE ' + conditions.map(key => `\`${key}\` = ?`).join(' AND ');
476
+ const values = Object.values(where).map(v => this._serializeValue(v));
477
+
478
+ return { whereClause, values };
479
+ }
480
+
481
+ /**
482
+ * Numerik alanları artırır (increment).
483
+ * @param {string} table - Verinin güncelleneceği tablo adı.
484
+ * @param {object} increments - Artırılacak alanlar ve miktarları.
485
+ * @param {object} where - Güncelleme koşulları.
486
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
487
+ */
488
+ async increment(table, increments, where = {}) {
489
+ const incrementClauses = Object.keys(increments).map(field =>
490
+ `${field} = ${field} + ?`
491
+ ).join(', ');
492
+
493
+ const incrementValues = Object.values(increments);
494
+ const { whereClause, values: whereValues } = this.buildWhereClause(where);
495
+
496
+ const sql = `UPDATE ${table} SET ${incrementClauses}${whereClause}`;
497
+ const allValues = [...incrementValues, ...whereValues];
498
+
499
+ const [result] = await this.connection.execute(sql, allValues);
500
+ return result.affectedRows;
501
+ }
502
+
503
+ /**
504
+ * Numerik alanları azaltır (decrement).
505
+ * @param {string} table - Verinin güncelleneceği tablo adı.
506
+ * @param {object} decrements - Azaltılacak alanlar ve miktarları.
507
+ * @param {object} where - Güncelleme koşulları.
508
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
509
+ */
510
+ async decrement(table, decrements, where = {}) {
511
+ const decrementClauses = Object.keys(decrements).map(field =>
512
+ `${field} = ${field} - ?`
513
+ ).join(', ');
514
+
515
+ const decrementValues = Object.values(decrements);
516
+ const { whereClause, values: whereValues } = this.buildWhereClause(where);
517
+
518
+ const sql = `UPDATE ${table} SET ${decrementClauses}${whereClause}`;
519
+ const allValues = [...decrementValues, ...whereValues];
520
+
521
+ const [result] = await this.connection.execute(sql, allValues);
522
+ return result.affectedRows;
523
+ }
463
524
  }
464
525
 
465
526
  module.exports = MySQLDatabase;
package/database/pg.js CHANGED
@@ -458,6 +458,70 @@ class PostgreSQLDatabase extends IDatabase {
458
458
  // No need for manual JSON parsing unlike MySQL
459
459
  return value;
460
460
  }
461
+
462
+ /**
463
+ * WHERE clause oluşturur (PostgreSQL $n syntax için)
464
+ * @param {object} where - WHERE koşulları
465
+ * @param {number} startIndex - Başlangıç parameter indeksi
466
+ * @returns {object} - whereClause string ve values array
467
+ */
468
+ buildWhereClause(where = {}, startIndex = 0) {
469
+ const conditions = Object.keys(where);
470
+ if (conditions.length === 0) {
471
+ return { whereClause: '', values: [] };
472
+ }
473
+
474
+ const whereClause = ' WHERE ' + conditions.map((key, index) =>
475
+ `${key} = $${startIndex + index + 1}`
476
+ ).join(' AND ');
477
+ const values = Object.values(where).map(v => this._serializeValue(v));
478
+
479
+ return { whereClause, values };
480
+ }
481
+
482
+ /**
483
+ * Numerik alanları artırır (increment).
484
+ * @param {string} table - Verinin güncelleneceği tablo adı.
485
+ * @param {object} increments - Artırılacak alanlar ve miktarları.
486
+ * @param {object} where - Güncelleme koşulları.
487
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
488
+ */
489
+ async increment(table, increments, where = {}) {
490
+ const incrementClauses = Object.keys(increments).map((field, index) =>
491
+ `${field} = ${field} + $${index + 1}`
492
+ ).join(', ');
493
+
494
+ const incrementValues = Object.values(increments);
495
+ const { whereClause, values: whereValues } = this.buildWhereClause(where, incrementValues.length);
496
+
497
+ const sql = `UPDATE ${table} SET ${incrementClauses}${whereClause}`;
498
+ const allValues = [...incrementValues, ...whereValues];
499
+
500
+ const result = await this.pool.query(sql, allValues);
501
+ return result.rowCount;
502
+ }
503
+
504
+ /**
505
+ * Numerik alanları azaltır (decrement).
506
+ * @param {string} table - Verinin güncelleneceği tablo adı.
507
+ * @param {object} decrements - Azaltılacak alanlar ve miktarları.
508
+ * @param {object} where - Güncelleme koşulları.
509
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
510
+ */
511
+ async decrement(table, decrements, where = {}) {
512
+ const decrementClauses = Object.keys(decrements).map((field, index) =>
513
+ `${field} = ${field} - $${index + 1}`
514
+ ).join(', ');
515
+
516
+ const decrementValues = Object.values(decrements);
517
+ const { whereClause, values: whereValues } = this.buildWhereClause(where, decrementValues.length);
518
+
519
+ const sql = `UPDATE ${table} SET ${decrementClauses}${whereClause}`;
520
+ const allValues = [...decrementValues, ...whereValues];
521
+
522
+ const result = await this.pool.query(sql, allValues);
523
+ return result.rowCount;
524
+ }
461
525
  }
462
526
 
463
527
  module.exports = PostgreSQLDatabase;