@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.
@@ -0,0 +1,342 @@
1
+ // redis.js - Redis Database Adapter (v4.x Uyumlu)
2
+ const { createClient } = require('redis');
3
+
4
+ class RedisDatabase {
5
+ constructor(config = {}) {
6
+ this.config = {
7
+ host: config.host || '127.0.0.1',
8
+ port: config.port || 6379,
9
+ password: config.password,
10
+ db: config.db || 0,
11
+ connectTimeout: config.connectTimeout || 5000,
12
+ commandTimeout: config.commandTimeout || 5000,
13
+ };
14
+
15
+ this.keyPrefix = config.keyPrefix || 'app:';
16
+ this.client = null;
17
+ this.isConnecting = false; // Eşzamanlı bağlantı girişimlerini önlemek için
18
+ }
19
+
20
+ async connect() {
21
+ if (this.client && this.client.isReady) {
22
+ return this.client;
23
+ }
24
+
25
+ // Eğer zaten bağlantı girişimi yapılıyorsa, bekle
26
+ if (this.isConnecting) {
27
+ while (this.isConnecting) {
28
+ await new Promise(resolve => setTimeout(resolve, 100));
29
+ }
30
+ return this.client;
31
+ }
32
+
33
+ this.isConnecting = true;
34
+
35
+ try {
36
+ this.client = createClient({
37
+ socket: {
38
+ host: this.config.host,
39
+ port: this.config.port,
40
+ connectTimeout: this.config.connectTimeout,
41
+ },
42
+ password: this.config.password,
43
+ database: this.config.db,
44
+ });
45
+
46
+ this.client.on('error', (err) => {
47
+ console.error('Redis Error:', err.message);
48
+ });
49
+
50
+ //this.client.on('connect', () => console.log('Redis Connected'));
51
+ //this.client.on('ready', () => console.log('Redis Ready'));
52
+ this.client.on('end', () => {
53
+ console.log('Redis Connection Ended');
54
+ this.client = null;
55
+ });
56
+
57
+ // Timeout ile bağlantı kontrolü
58
+ await Promise.race([
59
+ this.client.connect(),
60
+ new Promise((_, reject) =>
61
+ setTimeout(() => reject(new Error('Redis connection timeout')), this.config.connectTimeout)
62
+ ),
63
+ ]);
64
+
65
+ console.log('Redis bağlantısı başarılı');
66
+ } catch (err) {
67
+ this.client = null;
68
+ throw new Error(`Redis bağlantısı başarısız: ${err.message}`);
69
+ } finally {
70
+ this.isConnecting = false;
71
+ }
72
+
73
+ return this.client;
74
+ }
75
+
76
+ _getKey(table, id) {
77
+ return `${this.keyPrefix}${table}:${id}`;
78
+ }
79
+
80
+ _getTableKey(table) {
81
+ return `${this.keyPrefix}${table}:*`;
82
+ }
83
+
84
+ async select(table, where = {}) {
85
+ try {
86
+ const client = await this.connect();
87
+ const pattern = this._getTableKey(table);
88
+ const keys = await client.keys(pattern);
89
+
90
+ if (!keys.length) return [];
91
+
92
+ const values = await client.mGet(keys);
93
+ return values
94
+ .map(v => {
95
+ try {
96
+ return v ? JSON.parse(v) : null;
97
+ } catch (parseErr) {
98
+ console.error('JSON parse error:', parseErr);
99
+ return null;
100
+ }
101
+ })
102
+ .filter(Boolean)
103
+ .filter(item => Object.entries(where).every(([k, val]) => item[k] === val));
104
+ } catch (err) {
105
+ console.error('Select error:', err);
106
+ throw err;
107
+ }
108
+ }
109
+
110
+ async selectOne(table, where = {}) {
111
+ const results = await this.select(table, where);
112
+ return results.length ? results[0] : null;
113
+ }
114
+
115
+ async insert(table, data) {
116
+ try {
117
+ const client = await this.connect();
118
+ const insertData = { ...data };
119
+
120
+ if (!insertData.id) {
121
+ insertData.id = Date.now().toString() + Math.random().toString(36).slice(2, 9);
122
+ }
123
+
124
+ const key = this._getKey(table, insertData.id);
125
+ await client.set(key, JSON.stringify(insertData));
126
+ return insertData;
127
+ } catch (err) {
128
+ console.error('Insert error:', err);
129
+ throw err;
130
+ }
131
+ }
132
+
133
+ async update(table, data, where) {
134
+ try {
135
+ const existing = await this.select(table, where);
136
+ if (!existing.length) return [];
137
+
138
+ const client = await this.connect();
139
+ const updated = [];
140
+
141
+ for (const item of existing) {
142
+ const merged = { ...item, ...data };
143
+ await client.set(this._getKey(table, item.id), JSON.stringify(merged));
144
+ updated.push(merged);
145
+ }
146
+ return updated;
147
+ } catch (err) {
148
+ console.error('Update error:', err);
149
+ throw err;
150
+ }
151
+ }
152
+
153
+ async updateOne(table, data, where) {
154
+ const results = await this.update(table, data, where);
155
+ return results.length ? results[0] : null;
156
+ }
157
+
158
+ async set(table, data, where) {
159
+ try {
160
+ const existing = await this.selectOne(table, where);
161
+ if (existing) {
162
+ return await this.updateOne(table, data, where);
163
+ } else {
164
+ return await this.insert(table, { ...data, ...where });
165
+ }
166
+ } catch (err) {
167
+ console.error('Set error:', err);
168
+ throw err;
169
+ }
170
+ }
171
+
172
+ async delete(table, where) {
173
+ try {
174
+ const existing = await this.select(table, where);
175
+ if (!existing.length) return [];
176
+
177
+ const client = await this.connect();
178
+ const keys = existing.map(item => this._getKey(table, item.id));
179
+ await client.del(keys);
180
+ return existing;
181
+ } catch (err) {
182
+ console.error('Delete error:', err);
183
+ throw err;
184
+ }
185
+ }
186
+
187
+ async deleteOne(table, where) {
188
+ const results = await this.delete(table, where);
189
+ return results.length ? results[0] : null;
190
+ }
191
+
192
+ async bulkInsert(table, dataArray) {
193
+ if (!Array.isArray(dataArray) || !dataArray.length) {
194
+ return [];
195
+ }
196
+
197
+ try {
198
+ const results = [];
199
+ for (const data of dataArray) {
200
+ results.push(await this.insert(table, data));
201
+ }
202
+ return results;
203
+ } catch (err) {
204
+ console.error('Bulk insert error:', err);
205
+ throw err;
206
+ }
207
+ }
208
+
209
+ async ensureTable(table) {
210
+ // Redis'te tablo kavramı yoktur, her zaman true döner
211
+ return true;
212
+ }
213
+
214
+ async close() {
215
+ if (this.client) {
216
+ try {
217
+ await this.client.quit();
218
+ } catch (err) {
219
+ console.error('Redis close error:', err);
220
+ } finally {
221
+ this.client = null;
222
+ }
223
+ }
224
+ }
225
+
226
+ async ping() {
227
+ try {
228
+ const client = await this.connect();
229
+ return await client.ping();
230
+ } catch (err) {
231
+ console.error('Ping error:', err);
232
+ throw err;
233
+ }
234
+ }
235
+
236
+ async flushTable(table) {
237
+ try {
238
+ const client = await this.connect();
239
+ const pattern = this._getTableKey(table);
240
+ const keys = await client.keys(pattern);
241
+
242
+ if (keys.length > 0) {
243
+ await client.del(keys);
244
+ }
245
+
246
+ return keys.length;
247
+ } catch (err) {
248
+ console.error('Flush table error:', err);
249
+ throw err;
250
+ }
251
+ }
252
+
253
+ // Yardımcı metodlar
254
+ async getStats(table) {
255
+ try {
256
+ const client = await this.connect();
257
+ const pattern = this._getTableKey(table);
258
+ const keys = await client.keys(pattern);
259
+ return {
260
+ table,
261
+ keyCount: keys.length,
262
+ pattern
263
+ };
264
+ } catch (err) {
265
+ console.error('Get stats error:', err);
266
+ throw err;
267
+ }
268
+ }
269
+
270
+ async exists(table, where) {
271
+ const result = await this.selectOne(table, where);
272
+ return result !== null;
273
+ }
274
+
275
+ /**
276
+ * Numerik alanları artırır (increment).
277
+ * Redis için hash field'ları üzerinde HINCRBY kullanır.
278
+ * @param {string} table - Verinin güncelleneceği tablo adı.
279
+ * @param {object} increments - Artırılacak alanlar ve miktarları.
280
+ * @param {object} where - Güncelleme koşulları.
281
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
282
+ */
283
+ async increment(table, increments, where = {}) {
284
+ try {
285
+ const client = await this.connect();
286
+
287
+ // Önce mevcut kayıtları bul
288
+ const existingRecords = await this.select(table, where);
289
+ let affectedCount = 0;
290
+
291
+ for (const record of existingRecords) {
292
+ const key = this._getRecordKey(table, record._id);
293
+
294
+ // Her increment field için HINCRBY kullan
295
+ for (const [field, value] of Object.entries(increments)) {
296
+ await client.hIncrBy(key, field, value);
297
+ }
298
+ affectedCount++;
299
+ }
300
+
301
+ return affectedCount;
302
+ } catch (err) {
303
+ console.error('Increment error:', err);
304
+ throw err;
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Numerik alanları azaltır (decrement).
310
+ * Redis için hash field'ları üzerinde HINCRBY ile negatif değer kullanır.
311
+ * @param {string} table - Verinin güncelleneceği tablo adı.
312
+ * @param {object} decrements - Azaltılacak alanlar ve miktarları.
313
+ * @param {object} where - Güncelleme koşulları.
314
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
315
+ */
316
+ async decrement(table, decrements, where = {}) {
317
+ try {
318
+ const client = await this.connect();
319
+
320
+ // Önce mevcut kayıtları bul
321
+ const existingRecords = await this.select(table, where);
322
+ let affectedCount = 0;
323
+
324
+ for (const record of existingRecords) {
325
+ const key = this._getRecordKey(table, record._id);
326
+
327
+ // Her decrement field için HINCRBY ile negatif değer kullan
328
+ for (const [field, value] of Object.entries(decrements)) {
329
+ await client.hIncrBy(key, field, -value);
330
+ }
331
+ affectedCount++;
332
+ }
333
+
334
+ return affectedCount;
335
+ } catch (err) {
336
+ console.error('Decrement error:', err);
337
+ throw err;
338
+ }
339
+ }
340
+ }
341
+
342
+ module.exports = RedisDatabase;
@@ -466,6 +466,75 @@ class SQLiteDatabase extends IDatabase{
466
466
  });
467
467
  }
468
468
 
469
+ /**
470
+ * WHERE clause oluşturur
471
+ * @param {object} where - WHERE koşulları
472
+ * @returns {object} - whereClause string ve values array
473
+ */
474
+ buildWhereClause(where = {}) {
475
+ const conditions = Object.keys(where);
476
+ if (conditions.length === 0) {
477
+ return { whereClause: '', values: [] };
478
+ }
479
+
480
+ const whereClause = ' WHERE ' + conditions.map(key => `${key} = ?`).join(' AND ');
481
+ const values = Object.values(where);
482
+
483
+ return { whereClause, values };
484
+ }
485
+
486
+ /**
487
+ * Numerik alanları artırır (increment).
488
+ * @param {string} table - Verinin güncelleneceği tablo adı.
489
+ * @param {object} increments - Artırılacak alanlar ve miktarları.
490
+ * @param {object} where - Güncelleme koşulları.
491
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
492
+ */
493
+ async increment(table, increments, where = {}) {
494
+ const incrementClauses = Object.keys(increments).map(field =>
495
+ `${field} = ${field} + ?`
496
+ ).join(', ');
497
+
498
+ const incrementValues = Object.values(increments);
499
+ const { whereClause, values: whereValues } = this.buildWhereClause(where);
500
+
501
+ const sql = `UPDATE ${table} SET ${incrementClauses}${whereClause}`;
502
+ const allValues = [...incrementValues, ...whereValues];
503
+
504
+ return new Promise((resolve, reject) => {
505
+ this.db.run(sql, allValues, function(err) {
506
+ if (err) reject(err);
507
+ else resolve(this.changes);
508
+ });
509
+ });
510
+ }
511
+
512
+ /**
513
+ * Numerik alanları azaltır (decrement).
514
+ * @param {string} table - Verinin güncelleneceği tablo adı.
515
+ * @param {object} decrements - Azaltılacak alanlar ve miktarları.
516
+ * @param {object} where - Güncelleme koşulları.
517
+ * @returns {Promise<number>} Etkilenen kayıt sayısı.
518
+ */
519
+ async decrement(table, decrements, where = {}) {
520
+ const decrementClauses = Object.keys(decrements).map(field =>
521
+ `${field} = ${field} - ?`
522
+ ).join(', ');
523
+
524
+ const decrementValues = Object.values(decrements);
525
+ const { whereClause, values: whereValues } = this.buildWhereClause(where);
526
+
527
+ const sql = `UPDATE ${table} SET ${decrementClauses}${whereClause}`;
528
+ const allValues = [...decrementValues, ...whereValues];
529
+
530
+ return new Promise((resolve, reject) => {
531
+ this.db.run(sql, allValues, function(err) {
532
+ if (err) reject(err);
533
+ else resolve(this.changes);
534
+ });
535
+ });
536
+ }
537
+
469
538
  /**
470
539
  * Veritabanı bağlantısını kapatır.
471
540
  */
@@ -372,6 +372,256 @@ function generateSlug(text) {
372
372
  function wordCount(text) {
373
373
  return text.trim().split(/\s+/).length;
374
374
  }
375
+
376
+ // Validasyon İşlemleri
377
+ function isEmail(email) {
378
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
379
+ return emailRegex.test(email);
380
+ }
381
+
382
+ function isPhone(phone) {
383
+ const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
384
+ return phoneRegex.test(phone.replace(/[\s\-\(\)]/g, ''));
385
+ }
386
+
387
+ function isURL(url) {
388
+ try {
389
+ new URL(url);
390
+ return true;
391
+ } catch {
392
+ return false;
393
+ }
394
+ }
395
+
396
+ function sanitizeHTML(html) {
397
+ return html
398
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
399
+ .replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '')
400
+ .replace(/javascript:/gi, '')
401
+ .replace(/on\w+\s*=/gi, '');
402
+ }
403
+
404
+ function validateCreditCard(cardNumber) {
405
+ // Luhn algoritması
406
+ const cleanNumber = cardNumber.replace(/\D/g, '');
407
+ if (cleanNumber.length < 13 || cleanNumber.length > 19) return false;
408
+
409
+ let sum = 0;
410
+ let isEven = false;
411
+
412
+ for (let i = cleanNumber.length - 1; i >= 0; i--) {
413
+ let digit = parseInt(cleanNumber[i]);
414
+
415
+ if (isEven) {
416
+ digit *= 2;
417
+ if (digit > 9) digit -= 9;
418
+ }
419
+
420
+ sum += digit;
421
+ isEven = !isEven;
422
+ }
423
+
424
+ return sum % 10 === 0;
425
+ }
426
+
427
+ function validateSchema(data, schema) {
428
+ const errors = [];
429
+
430
+ for (const [key, rules] of Object.entries(schema)) {
431
+ const value = data[key];
432
+
433
+ if (rules.required && (value === undefined || value === null || value === '')) {
434
+ errors.push(`${key} alanı zorunludur`);
435
+ continue;
436
+ }
437
+
438
+ if (value !== undefined && value !== null) {
439
+ if (rules.type && typeof value !== rules.type) {
440
+ errors.push(`${key} alanı ${rules.type} tipinde olmalıdır`);
441
+ }
442
+
443
+ if (rules.minLength && value.length < rules.minLength) {
444
+ errors.push(`${key} en az ${rules.minLength} karakter olmalıdır`);
445
+ }
446
+
447
+ if (rules.maxLength && value.length > rules.maxLength) {
448
+ errors.push(`${key} en fazla ${rules.maxLength} karakter olmalıdır`);
449
+ }
450
+
451
+ if (rules.pattern && !rules.pattern.test(value)) {
452
+ errors.push(`${key} geçerli formatta değil`);
453
+ }
454
+
455
+ if (rules.min && value < rules.min) {
456
+ errors.push(`${key} en az ${rules.min} olmalıdır`);
457
+ }
458
+
459
+ if (rules.max && value > rules.max) {
460
+ errors.push(`${key} en fazla ${rules.max} olmalıdır`);
461
+ }
462
+ }
463
+ }
464
+
465
+ return {
466
+ isValid: errors.length === 0,
467
+ errors
468
+ };
469
+ }
470
+
471
+ function sanitizeInput(input, options = {}) {
472
+ if (typeof input !== 'string') return input;
473
+
474
+ let sanitized = input;
475
+
476
+ if (options.trim !== false) {
477
+ sanitized = sanitized.trim();
478
+ }
479
+
480
+ if (options.removeHTML) {
481
+ sanitized = sanitizeHTML(sanitized);
482
+ }
483
+
484
+ if (options.escape) {
485
+ sanitized = sanitized
486
+ .replace(/&/g, '&amp;')
487
+ .replace(/</g, '&lt;')
488
+ .replace(/>/g, '&gt;')
489
+ .replace(/"/g, '&quot;')
490
+ .replace(/'/g, '&#x27;');
491
+ }
492
+
493
+ return sanitized;
494
+ }
495
+
496
+ // Logger İşlemleri
497
+ class Logger {
498
+ constructor(options = {}) {
499
+ this.level = options.level || 'info';
500
+ this.enableColors = options.enableColors !== false;
501
+ this.enableTimestamp = options.enableTimestamp !== false;
502
+ this.logFile = options.logFile || null;
503
+ this.levels = {
504
+ error: 0,
505
+ warn: 1,
506
+ info: 2,
507
+ debug: 3
508
+ };
509
+ this.colors = {
510
+ error: '\x1b[31m', // Red
511
+ warn: '\x1b[33m', // Yellow
512
+ info: '\x1b[36m', // Cyan
513
+ debug: '\x1b[35m', // Magenta
514
+ reset: '\x1b[0m'
515
+ };
516
+ }
517
+
518
+ shouldLog(level) {
519
+ return this.levels[level] <= this.levels[this.level];
520
+ }
521
+
522
+ formatMessage(level, message, data) {
523
+ let formatted = '';
524
+
525
+ if (this.enableTimestamp) {
526
+ formatted += `[${new Date().toISOString()}] `;
527
+ }
528
+
529
+ if (this.enableColors) {
530
+ formatted += `${this.colors[level]}[${level.toUpperCase()}]${this.colors.reset} `;
531
+ } else {
532
+ formatted += `[${level.toUpperCase()}] `;
533
+ }
534
+
535
+ formatted += message;
536
+
537
+ if (data !== undefined) {
538
+ formatted += ' ' + (typeof data === 'object' ? JSON.stringify(data, null, 2) : data);
539
+ }
540
+
541
+ return formatted;
542
+ }
543
+
544
+ writeToFile(message) {
545
+ if (this.logFile) {
546
+ const fs = require('fs');
547
+ const timestamp = new Date().toISOString();
548
+ const logEntry = `${timestamp} ${message}\n`;
549
+
550
+ try {
551
+ fs.appendFileSync(this.logFile, logEntry);
552
+ } catch (error) {
553
+ console.error('Log dosyasına yazılamadı:', error.message);
554
+ }
555
+ }
556
+ }
557
+
558
+ log(level, message, data) {
559
+ if (!this.shouldLog(level)) return;
560
+
561
+ const formatted = this.formatMessage(level, message, data);
562
+
563
+ if (level === 'error') {
564
+ console.error(formatted);
565
+ } else if (level === 'warn') {
566
+ console.warn(formatted);
567
+ } else {
568
+ console.log(formatted);
569
+ }
570
+
571
+ this.writeToFile(formatted);
572
+ }
573
+
574
+ error(message, data) {
575
+ this.log('error', message, data);
576
+ }
577
+
578
+ warn(message, data) {
579
+ this.log('warn', message, data);
580
+ }
581
+
582
+ info(message, data) {
583
+ this.log('info', message, data);
584
+ }
585
+
586
+ debug(message, data) {
587
+ this.log('debug', message, data);
588
+ }
589
+
590
+ setLevel(level) {
591
+ if (this.levels.hasOwnProperty(level)) {
592
+ this.level = level;
593
+ } else {
594
+ throw new Error(`Geçersiz log seviyesi: ${level}`);
595
+ }
596
+ }
597
+
598
+ getLevel() {
599
+ return this.level;
600
+ }
601
+ }
602
+
603
+ function createLogger(options = {}) {
604
+ return new Logger(options);
605
+ }
606
+
607
+ // Varsayılan logger instance
608
+ const defaultLogger = new Logger();
609
+
610
+ function logInfo(message, data) {
611
+ defaultLogger.info(message, data);
612
+ }
613
+
614
+ function logError(message, data) {
615
+ defaultLogger.error(message, data);
616
+ }
617
+
618
+ function logWarn(message, data) {
619
+ defaultLogger.warn(message, data);
620
+ }
621
+
622
+ function logDebug(message, data) {
623
+ defaultLogger.debug(message, data);
624
+ }
375
625
  // Exportlar
376
626
  module.exports = {
377
627
  http: {
@@ -435,4 +685,21 @@ module.exports = {
435
685
  combination,
436
686
  permutation,
437
687
  },
688
+ validation: {
689
+ isEmail,
690
+ isPhone,
691
+ isURL,
692
+ sanitizeHTML,
693
+ validateCreditCard,
694
+ validateSchema,
695
+ sanitizeInput,
696
+ },
697
+ logger: {
698
+ createLogger,
699
+ Logger,
700
+ info: logInfo,
701
+ error: logError,
702
+ warn: logWarn,
703
+ debug: logDebug,
704
+ },
438
705
  };