student-help 2.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,697 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import * as bcrypt from 'bcrypt';
3
+ import * as crypto from 'crypto';
4
+
5
+ /**
6
+ * ============================================
7
+ * STUDENT HELP SERVICE - Comprehensive Utilities
8
+ * ============================================
9
+ * Global service with functions to solve various problems
10
+ */
11
+
12
+ // ========== INTERFACES ==========
13
+ export interface ValidationResult {
14
+ valid: boolean;
15
+ message: string;
16
+ errors?: string[];
17
+ }
18
+
19
+ export interface FileUploadResult {
20
+ success: boolean;
21
+ message: string;
22
+ filename?: string;
23
+ size?: number;
24
+ path?: string;
25
+ }
26
+
27
+ export interface ExportResult {
28
+ success: boolean;
29
+ message: string;
30
+ data?: string;
31
+ filename?: string;
32
+ }
33
+
34
+ export interface EmailNotification {
35
+ to: string;
36
+ subject: string;
37
+ message: string;
38
+ html?: string;
39
+ }
40
+
41
+ export interface StudentUser {
42
+ id?: string;
43
+ email: string;
44
+ name: string;
45
+ password?: string;
46
+ }
47
+
48
+ export interface AuthResponse {
49
+ success: boolean;
50
+ message: string;
51
+ token?: string;
52
+ user?: StudentUser;
53
+ }
54
+
55
+ // ========== MAIN SERVICE ==========
56
+ @Injectable()
57
+ export class StudentHelpService {
58
+ private users: Map<string, StudentUser> = new Map();
59
+ private sessions: Map<string, AuthResponse> = new Map();
60
+
61
+ /**
62
+ * ========================================
63
+ * 🔐 AUTHENTICATION & SECURITY
64
+ * ========================================
65
+ */
66
+
67
+ /**
68
+ * Hash a password with bcrypt
69
+ */
70
+ async hashPassword(password: string, rounds: number = 10): Promise<string> {
71
+ return bcrypt.hash(password, rounds);
72
+ }
73
+
74
+ /**
75
+ * Compare password with hash
76
+ */
77
+ async comparePassword(password: string, hash: string): Promise<boolean> {
78
+ return bcrypt.compare(password, hash);
79
+ }
80
+
81
+ /**
82
+ * Generate JWT-like token
83
+ */
84
+ generateToken(payload: any, secret: string = process.env.JWT_SECRET || 'default-secret'): string {
85
+ const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url');
86
+ const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
87
+ const signature = crypto
88
+ .createHmac('sha256', secret)
89
+ .update(`${header}.${body}`)
90
+ .digest('base64url');
91
+
92
+ return `${header}.${body}.${signature}`;
93
+ }
94
+
95
+ /**
96
+ * Generate random token
97
+ */
98
+ generateRandomToken(length: number = 32): string {
99
+ return crypto.randomBytes(length).toString('hex');
100
+ }
101
+
102
+ /**
103
+ * Encrypt text with AES-256
104
+ */
105
+ encryptText(text: string, key: string = process.env.ENCRYPTION_KEY || 'default-key'): string {
106
+ const cipher = crypto.createCipher('aes-256-cbc', key);
107
+ let encrypted = cipher.update(text, 'utf8', 'hex');
108
+ encrypted += cipher.final('hex');
109
+ return encrypted;
110
+ }
111
+
112
+ /**
113
+ * Decrypt text
114
+ */
115
+ decryptText(encrypted: string, key: string = process.env.ENCRYPTION_KEY || 'default-key'): string {
116
+ const decipher = crypto.createDecipher('aes-256-cbc', key);
117
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
118
+ decrypted += decipher.final('utf8');
119
+ return decrypted;
120
+ }
121
+
122
+ /**
123
+ * Generate API Key
124
+ */
125
+ generateApiKey(): string {
126
+ return 'sk_' + crypto.randomBytes(32).toString('hex');
127
+ }
128
+
129
+ /**
130
+ * Register a new user
131
+ */
132
+ async register(email: string, name: string, password: string): Promise<AuthResponse> {
133
+ try {
134
+ if (this._getUserByEmail(email)) {
135
+ return { success: false, message: 'User already exists' };
136
+ }
137
+
138
+ const hashedPassword = await bcrypt.hash(password, 10);
139
+ const userId = `user_${Date.now()}`;
140
+ const user: StudentUser = { id: userId, email, name, password: hashedPassword };
141
+
142
+ this.users.set(userId, user);
143
+
144
+ return {
145
+ success: true,
146
+ message: 'User registered successfully',
147
+ user: { id: userId, email, name }
148
+ };
149
+ } catch (error) {
150
+ return { success: false, message: `Registration failed: ${error.message}` };
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Login a user
156
+ */
157
+ async login(email: string, password: string): Promise<AuthResponse> {
158
+ try {
159
+ const user = this._getUserByEmail(email);
160
+
161
+ if (!user || !user.password) {
162
+ return { success: false, message: 'Invalid credentials' };
163
+ }
164
+
165
+ const isPasswordValid = await bcrypt.compare(password, user.password);
166
+
167
+ if (!isPasswordValid) {
168
+ return { success: false, message: 'Invalid credentials' };
169
+ }
170
+
171
+ const response: AuthResponse = {
172
+ success: true,
173
+ message: 'Login successful',
174
+ user: { id: user.id, email: user.email, name: user.name }
175
+ };
176
+
177
+ this.sessions.set(user.id || '', response);
178
+ return response;
179
+ } catch (error) {
180
+ return { success: false, message: `Login failed: ${error.message}` };
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Logout a user
186
+ */
187
+ async logout(userId: string): Promise<AuthResponse> {
188
+ try {
189
+ this.sessions.delete(userId);
190
+ return { success: true, message: 'Logout successful' };
191
+ } catch (error) {
192
+ return { success: false, message: `Logout failed: ${error.message}` };
193
+ }
194
+ }
195
+
196
+ /**
197
+ * ========================================
198
+ * 📧 EMAIL & NOTIFICATIONS
199
+ * ========================================
200
+ */
201
+
202
+ /**
203
+ * Send email notification
204
+ */
205
+ async sendEmail(notification: EmailNotification): Promise<{ success: boolean; message: string }> {
206
+ try {
207
+ console.log(`📧 Email sent to ${notification.to}`);
208
+ return { success: true, message: `Email sent to ${notification.to}` };
209
+ } catch (error) {
210
+ return { success: false, message: `Failed to send email: ${error.message}` };
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Send SMS notification
216
+ */
217
+ async sendSMS(phoneNumber: string, message: string): Promise<{ success: boolean; message: string }> {
218
+ try {
219
+ console.log(`📱 SMS sent to ${phoneNumber}`);
220
+ return { success: true, message: `SMS sent to ${phoneNumber}` };
221
+ } catch (error) {
222
+ return { success: false, message: `Failed to send SMS: ${error.message}` };
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Send webhook notification
228
+ */
229
+ async sendWebhook(url: string, data: any): Promise<{ success: boolean; message: string }> {
230
+ try {
231
+ console.log(`🔔 Webhook sent to ${url}`);
232
+ return { success: true, message: `Webhook sent to ${url}` };
233
+ } catch (error) {
234
+ return { success: false, message: `Failed to send webhook: ${error.message}` };
235
+ }
236
+ }
237
+
238
+ /**
239
+ * ========================================
240
+ * 📄 DATA VALIDATION
241
+ * ========================================
242
+ */
243
+
244
+ /**
245
+ * Validate email
246
+ */
247
+ validateEmail(email: string): ValidationResult {
248
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
249
+ const valid = emailRegex.test(email);
250
+
251
+ return { valid, message: valid ? 'Valid email' : 'Invalid email' };
252
+ }
253
+
254
+ /**
255
+ * Validate password strength
256
+ */
257
+ validatePasswordStrength(password: string): ValidationResult {
258
+ const errors: string[] = [];
259
+
260
+ if (password.length < 8) errors.push('Password must be at least 8 characters');
261
+ if (!/[A-Z]/.test(password)) errors.push('Password must contain uppercase letters');
262
+ if (!/[a-z]/.test(password)) errors.push('Password must contain lowercase letters');
263
+ if (!/[0-9]/.test(password)) errors.push('Password must contain numbers');
264
+ if (!/[!@#$%^&*]/.test(password)) errors.push('Password must contain special characters');
265
+
266
+ return {
267
+ valid: errors.length === 0,
268
+ message: errors.length === 0 ? 'Strong password' : 'Weak password',
269
+ errors
270
+ };
271
+ }
272
+
273
+ /**
274
+ * Validate CPF (Brazilian)
275
+ */
276
+ validateCPF(cpf: string): ValidationResult {
277
+ const cleanCPF = cpf.replace(/\D/g, '');
278
+
279
+ if (cleanCPF.length !== 11 || /^(\d)\1{10}$/.test(cleanCPF)) {
280
+ return { valid: false, message: 'Invalid CPF' };
281
+ }
282
+
283
+ const calculate = (s: number): number => {
284
+ let sum = 0;
285
+ let multiplier = s + 1;
286
+ for (let i = 0; i < s; i++) {
287
+ sum += parseInt(cleanCPF.charAt(i)) * multiplier--;
288
+ }
289
+ const remainder = (sum * 10) % 11;
290
+ return remainder === 10 ? 0 : remainder;
291
+ };
292
+
293
+ const digit1 = calculate(9);
294
+ const digit2 = calculate(10);
295
+ const isValid = parseInt(cleanCPF.charAt(9)) === digit1 && parseInt(cleanCPF.charAt(10)) === digit2;
296
+
297
+ return { valid: isValid, message: isValid ? 'Valid CPF' : 'Invalid CPF' };
298
+ }
299
+
300
+ /**
301
+ * Validate phone number
302
+ */
303
+ validatePhoneNumber(phone: string): ValidationResult {
304
+ const cleanPhone = phone.replace(/\D/g, '');
305
+ const valid = cleanPhone.length >= 10 && cleanPhone.length <= 15;
306
+
307
+ return { valid, message: valid ? 'Valid phone number' : 'Invalid phone number' };
308
+ }
309
+
310
+ /**
311
+ * Validate URL
312
+ */
313
+ validateURL(url: string): ValidationResult {
314
+ try {
315
+ new URL(url);
316
+ return { valid: true, message: 'Valid URL' };
317
+ } catch {
318
+ return { valid: false, message: 'Invalid URL' };
319
+ }
320
+ }
321
+
322
+ /**
323
+ * ========================================
324
+ * 📝 DATA TRANSFORMATION & SANITIZATION
325
+ * ========================================
326
+ */
327
+
328
+ /**
329
+ * Sanitize string
330
+ */
331
+ sanitizeString(str: string): string {
332
+ return str.replace(/<[^>]*>/g, '').trim().substring(0, 1000);
333
+ }
334
+
335
+ /**
336
+ * Capitalize first letter
337
+ */
338
+ capitalize(str: string): string {
339
+ return str.charAt(0).toUpperCase() + str.slice(1);
340
+ }
341
+
342
+ /**
343
+ * Convert to slug
344
+ */
345
+ toSlug(str: string): string {
346
+ return str
347
+ .toLowerCase()
348
+ .replace(/[^\w\s-]/g, '')
349
+ .replace(/[\s_]+/g, '-')
350
+ .replace(/^-+|-+$/g, '');
351
+ }
352
+
353
+ /**
354
+ * Format currency
355
+ */
356
+ formatCurrency(amount: number, currency: string = 'BRL'): string {
357
+ return new Intl.NumberFormat('pt-BR', { style: 'currency', currency }).format(amount);
358
+ }
359
+
360
+ /**
361
+ * Format date
362
+ */
363
+ formatDate(date: Date, format: string = 'DD/MM/YYYY'): string {
364
+ const day = String(date.getDate()).padStart(2, '0');
365
+ const month = String(date.getMonth() + 1).padStart(2, '0');
366
+ const year = date.getFullYear();
367
+
368
+ return format.replace('DD', day).replace('MM', month).replace('YYYY', String(year));
369
+ }
370
+
371
+ /**
372
+ * Parse JSON safely
373
+ */
374
+ safeParseJSON(jsonString: string, defaultValue: any = null): any {
375
+ try {
376
+ return JSON.parse(jsonString);
377
+ } catch {
378
+ return defaultValue;
379
+ }
380
+ }
381
+
382
+ /**
383
+ * ========================================
384
+ * 📊 DATA EXPORT
385
+ * ========================================
386
+ */
387
+
388
+ /**
389
+ * Generate CSV from array of objects
390
+ */
391
+ generateCSV(data: any[], filename: string = 'export.csv'): ExportResult {
392
+ try {
393
+ if (!data || data.length === 0) {
394
+ return { success: false, message: 'No data to export' };
395
+ }
396
+
397
+ const keys = Object.keys(data[0]);
398
+ const header = keys.join(',');
399
+ const rows = data.map(obj =>
400
+ keys.map(key => `"${String(obj[key] || '').replace(/"/g, '""')}"`).join(',')
401
+ );
402
+
403
+ const csv = [header, ...rows].join('\n');
404
+
405
+ return { success: true, message: 'CSV generated successfully', data: csv, filename };
406
+ } catch (error) {
407
+ return { success: false, message: `Failed to generate CSV: ${error.message}` };
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Generate JSON from data
413
+ */
414
+ generateJSON(data: any, filename: string = 'export.json'): ExportResult {
415
+ try {
416
+ const json = JSON.stringify(data, null, 2);
417
+ return { success: true, message: 'JSON generated successfully', data: json, filename };
418
+ } catch (error) {
419
+ return { success: false, message: `Failed to generate JSON: ${error.message}` };
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Generate basic report
425
+ */
426
+ generateReport(title: string, content: any): ExportResult {
427
+ try {
428
+ const report = `
429
+ ===============================================
430
+ ${title}
431
+ ===============================================
432
+ Generated at: ${new Date().toLocaleString('pt-BR')}
433
+
434
+ ${JSON.stringify(content, null, 2)}
435
+
436
+ ===============================================
437
+ `.trim();
438
+
439
+ return { success: true, message: 'Report generated successfully', data: report, filename: `report_${Date.now()}.txt` };
440
+ } catch (error) {
441
+ return { success: false, message: `Failed to generate report: ${error.message}` };
442
+ }
443
+ }
444
+
445
+ /**
446
+ * ========================================
447
+ * 📁 FILE UPLOAD & PROCESSING
448
+ * ========================================
449
+ */
450
+
451
+ /**
452
+ * Validate file upload
453
+ */
454
+ validateFileUpload(filename: string, size: number, allowedTypes: string[] = ['jpg', 'png', 'pdf']): FileUploadResult {
455
+ const maxSize = 10 * 1024 * 1024;
456
+
457
+ if (size > maxSize) {
458
+ return { success: false, message: `File size exceeds limit (${maxSize / 1024 / 1024}MB)` };
459
+ }
460
+
461
+ const ext = filename.split('.').pop()?.toLowerCase() || '';
462
+
463
+ if (!allowedTypes.includes(ext)) {
464
+ return { success: false, message: `File type not allowed. Allowed: ${allowedTypes.join(', ')}` };
465
+ }
466
+
467
+ return { success: true, message: 'File is valid' };
468
+ }
469
+
470
+ /**
471
+ * Generate safe filename
472
+ */
473
+ generateSafeFilename(originalFilename: string): string {
474
+ const sanitized = originalFilename.replace(/[^a-zA-Z0-9.-]/g, '_');
475
+ const timestamp = Date.now();
476
+ const ext = sanitized.split('.').pop();
477
+
478
+ return `${timestamp}_${sanitized.split('.')[0]}.${ext}`;
479
+ }
480
+
481
+ /**
482
+ * Process image
483
+ */
484
+ async processImage(filename: string, width?: number, height?: number): Promise<FileUploadResult> {
485
+ return { success: true, message: 'Image processed successfully', filename: `resized_${filename}` };
486
+ }
487
+
488
+ /**
489
+ * ========================================
490
+ * 🧮 MATHEMATICAL & CONVERSION UTILITIES
491
+ * ========================================
492
+ */
493
+
494
+ /**
495
+ * Calculate percentage
496
+ */
497
+ calculatePercentage(value: number, total: number): number {
498
+ return total === 0 ? 0 : (value / total) * 100;
499
+ }
500
+
501
+ /**
502
+ * Calculate discount
503
+ */
504
+ calculateDiscount(originalPrice: number, discountPercent: number): number {
505
+ return originalPrice * (1 - discountPercent / 100);
506
+ }
507
+
508
+ /**
509
+ * Calculate compound interest
510
+ */
511
+ calculateCompoundInterest(principal: number, rate: number, time: number, compounds: number = 12): number {
512
+ return principal * Math.pow(1 + rate / 100 / compounds, compounds * time);
513
+ }
514
+
515
+ /**
516
+ * Convert temperature
517
+ */
518
+ convertTemperature(value: number, from: 'C' | 'F' | 'K', to: 'C' | 'F' | 'K'): number {
519
+ let celsius: number;
520
+
521
+ if (from === 'C') {
522
+ celsius = value;
523
+ } else if (from === 'F') {
524
+ celsius = (value - 32) * (5 / 9);
525
+ } else {
526
+ celsius = value - 273.15;
527
+ }
528
+
529
+ if (to === 'C') {
530
+ return Math.round(celsius * 100) / 100;
531
+ } else if (to === 'F') {
532
+ return Math.round((celsius * 9) / 5 + 32 * 100) / 100;
533
+ } else {
534
+ return Math.round((celsius + 273.15) * 100) / 100;
535
+ }
536
+ }
537
+
538
+ /**
539
+ * Convert distance units
540
+ */
541
+ convertDistance(value: number, from: 'mm' | 'cm' | 'm' | 'km', to: 'mm' | 'cm' | 'm' | 'km'): number {
542
+ const toMm = { mm: 1, cm: 10, m: 1000, km: 1000000 };
543
+ const valueInMm = value * toMm[from];
544
+ return valueInMm / toMm[to];
545
+ }
546
+
547
+ /**
548
+ * Generate random number
549
+ */
550
+ randomNumber(min: number, max: number): number {
551
+ return Math.floor(Math.random() * (max - min + 1)) + min;
552
+ }
553
+
554
+ /**
555
+ * Generate random string
556
+ */
557
+ randomString(length: number = 10): string {
558
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
559
+ let result = '';
560
+
561
+ for (let i = 0; i < length; i++) {
562
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
563
+ }
564
+
565
+ return result;
566
+ }
567
+
568
+ /**
569
+ * ========================================
570
+ * 🛠️ GENERAL UTILITIES
571
+ * ========================================
572
+ */
573
+
574
+ /**
575
+ * Delay execution (sleep)
576
+ */
577
+ sleep(milliseconds: number): Promise<void> {
578
+ return new Promise(resolve => setTimeout(resolve, milliseconds));
579
+ }
580
+
581
+ /**
582
+ * Retry function with exponential backoff
583
+ */
584
+ async retry<T>(fn: () => Promise<T>, maxAttempts: number = 3, delayMs: number = 1000): Promise<T> {
585
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
586
+ try {
587
+ return await fn();
588
+ } catch (error) {
589
+ if (attempt === maxAttempts) throw error;
590
+ await this.sleep(delayMs * attempt);
591
+ }
592
+ }
593
+ throw new Error('Max retries exceeded');
594
+ }
595
+
596
+ /**
597
+ * Get user by ID
598
+ */
599
+ getUser(userId: string): StudentUser | null {
600
+ const user = this.users.get(userId);
601
+ if (user) {
602
+ const { password, ...userWithoutPassword } = user;
603
+ return userWithoutPassword as StudentUser;
604
+ }
605
+ return null;
606
+ }
607
+
608
+ /**
609
+ * Get all users
610
+ */
611
+ getAllUsers(): StudentUser[] {
612
+ const users: StudentUser[] = [];
613
+ this.users.forEach((user) => {
614
+ const { password, ...userWithoutPassword } = user;
615
+ users.push(userWithoutPassword as StudentUser);
616
+ });
617
+ return users;
618
+ }
619
+
620
+ /**
621
+ * Update user profile
622
+ */
623
+ async updateUser(userId: string, updateData: Partial<StudentUser>): Promise<AuthResponse> {
624
+ try {
625
+ const user = this.users.get(userId);
626
+ if (!user) {
627
+ return { success: false, message: 'User not found' };
628
+ }
629
+
630
+ if (updateData.name) user.name = updateData.name;
631
+ if (updateData.email) user.email = updateData.email;
632
+
633
+ return { success: true, message: 'User updated successfully', user: { id: user.id, email: user.email, name: user.name } };
634
+ } catch (error) {
635
+ return { success: false, message: `Update failed: ${error.message}` };
636
+ }
637
+ }
638
+
639
+ /**
640
+ * Delete user
641
+ */
642
+ async deleteUser(userId: string): Promise<AuthResponse> {
643
+ try {
644
+ const user = this.users.get(userId);
645
+ if (!user) {
646
+ return { success: false, message: 'User not found' };
647
+ }
648
+
649
+ this.users.delete(userId);
650
+ this.sessions.delete(userId);
651
+
652
+ return { success: true, message: 'User deleted successfully' };
653
+ } catch (error) {
654
+ return { success: false, message: `Delete failed: ${error.message}` };
655
+ }
656
+ }
657
+
658
+ /**
659
+ * Check if user is authenticated
660
+ */
661
+ isAuthenticated(userId: string): boolean {
662
+ return this.sessions.has(userId);
663
+ }
664
+
665
+ /**
666
+ * Get user by email (private helper)
667
+ */
668
+ private _getUserByEmail(email: string): StudentUser | null {
669
+ for (const user of this.users.values()) {
670
+ if (user.email === email) {
671
+ return user;
672
+ }
673
+ }
674
+ return null;
675
+ }
676
+
677
+ /**
678
+ * Get service info
679
+ */
680
+ getInfo() {
681
+ return {
682
+ name: 'StudentHelp',
683
+ version: '2.0.0',
684
+ description: 'Complete utility service with multiple helpful functions',
685
+ modules: [
686
+ '🔐 Authentication & Security (hash, encrypt, tokens)',
687
+ '📧 Email & Notifications (email, SMS, webhooks)',
688
+ '📄 Data Validation (email, password, CPF, phone, URL)',
689
+ '📝 Data Transformation (sanitize, format, transform)',
690
+ '📊 Data Export (CSV, JSON, reports)',
691
+ '📁 File Upload (validation, processing)',
692
+ '🧮 Math & Conversions (percentage, interest, temperature, distance)',
693
+ '🛠️ General Utilities (sleep, retry, random)'
694
+ ]
695
+ };
696
+ }
697
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Página de listagem de Resource
3
+ * =====================================
4
+ * Esta página exibe todos os registros de Resource.
5
+ * Expansão:
6
+ * - Adicione filtros
7
+ * - Adicione paginação
8
+ * - Integre com auth se necessário
9
+ */
10
+ import React from 'react';
11
+ export default function ResourcePage() {
12
+ return <div>Resource CRUD Page</div>;
13
+ }