@nitronjs/framework 0.1.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.
Files changed (87) hide show
  1. package/README.md +429 -0
  2. package/cli/create.js +260 -0
  3. package/cli/njs.js +164 -0
  4. package/lib/Auth/Manager.js +111 -0
  5. package/lib/Build/Manager.js +1232 -0
  6. package/lib/Console/Commands/BuildCommand.js +25 -0
  7. package/lib/Console/Commands/DevCommand.js +385 -0
  8. package/lib/Console/Commands/MakeCommand.js +110 -0
  9. package/lib/Console/Commands/MigrateCommand.js +98 -0
  10. package/lib/Console/Commands/MigrateFreshCommand.js +97 -0
  11. package/lib/Console/Commands/SeedCommand.js +92 -0
  12. package/lib/Console/Commands/StorageLinkCommand.js +31 -0
  13. package/lib/Console/Stubs/controller.js +19 -0
  14. package/lib/Console/Stubs/middleware.js +9 -0
  15. package/lib/Console/Stubs/migration.js +23 -0
  16. package/lib/Console/Stubs/model.js +7 -0
  17. package/lib/Console/Stubs/page-hydration.tsx +54 -0
  18. package/lib/Console/Stubs/seeder.js +9 -0
  19. package/lib/Console/Stubs/vendor.tsx +11 -0
  20. package/lib/Core/Config.js +86 -0
  21. package/lib/Core/Environment.js +21 -0
  22. package/lib/Core/Paths.js +188 -0
  23. package/lib/Database/Connection.js +61 -0
  24. package/lib/Database/DB.js +84 -0
  25. package/lib/Database/Drivers/MySQLDriver.js +234 -0
  26. package/lib/Database/Manager.js +162 -0
  27. package/lib/Database/Model.js +161 -0
  28. package/lib/Database/QueryBuilder.js +714 -0
  29. package/lib/Database/QueryValidation.js +62 -0
  30. package/lib/Database/Schema/Blueprint.js +126 -0
  31. package/lib/Database/Schema/Manager.js +116 -0
  32. package/lib/Date/DateTime.js +108 -0
  33. package/lib/Date/Locale.js +68 -0
  34. package/lib/Encryption/Manager.js +47 -0
  35. package/lib/Filesystem/Manager.js +49 -0
  36. package/lib/Hashing/Manager.js +25 -0
  37. package/lib/Http/Server.js +317 -0
  38. package/lib/Logging/Manager.js +153 -0
  39. package/lib/Mail/Manager.js +120 -0
  40. package/lib/Route/Loader.js +81 -0
  41. package/lib/Route/Manager.js +265 -0
  42. package/lib/Runtime/Entry.js +11 -0
  43. package/lib/Session/File.js +299 -0
  44. package/lib/Session/Manager.js +259 -0
  45. package/lib/Session/Memory.js +67 -0
  46. package/lib/Session/Session.js +196 -0
  47. package/lib/Support/Str.js +100 -0
  48. package/lib/Translation/Manager.js +49 -0
  49. package/lib/Validation/MimeTypes.js +39 -0
  50. package/lib/Validation/Validator.js +691 -0
  51. package/lib/View/Manager.js +544 -0
  52. package/lib/View/Templates/default/Home.tsx +262 -0
  53. package/lib/View/Templates/default/MainLayout.tsx +44 -0
  54. package/lib/View/Templates/errors/404.tsx +13 -0
  55. package/lib/View/Templates/errors/500.tsx +13 -0
  56. package/lib/View/Templates/errors/ErrorLayout.tsx +112 -0
  57. package/lib/View/Templates/messages/Maintenance.tsx +17 -0
  58. package/lib/View/Templates/messages/MessageLayout.tsx +136 -0
  59. package/lib/index.js +57 -0
  60. package/package.json +47 -0
  61. package/skeleton/.env.example +26 -0
  62. package/skeleton/app/Controllers/HomeController.js +9 -0
  63. package/skeleton/app/Kernel.js +11 -0
  64. package/skeleton/app/Middlewares/Authentication.js +9 -0
  65. package/skeleton/app/Middlewares/Guest.js +9 -0
  66. package/skeleton/app/Middlewares/VerifyCsrf.js +24 -0
  67. package/skeleton/app/Models/User.js +7 -0
  68. package/skeleton/config/app.js +4 -0
  69. package/skeleton/config/auth.js +16 -0
  70. package/skeleton/config/database.js +27 -0
  71. package/skeleton/config/hash.js +3 -0
  72. package/skeleton/config/server.js +28 -0
  73. package/skeleton/config/session.js +21 -0
  74. package/skeleton/database/migrations/2025_01_01_00_00_users.js +20 -0
  75. package/skeleton/database/seeders/UserSeeder.js +15 -0
  76. package/skeleton/globals.d.ts +1 -0
  77. package/skeleton/package.json +24 -0
  78. package/skeleton/public/.gitkeep +0 -0
  79. package/skeleton/resources/css/.gitkeep +0 -0
  80. package/skeleton/resources/langs/.gitkeep +0 -0
  81. package/skeleton/resources/views/Site/Home.tsx +66 -0
  82. package/skeleton/routes/web.js +4 -0
  83. package/skeleton/storage/app/private/.gitkeep +0 -0
  84. package/skeleton/storage/app/public/.gitkeep +0 -0
  85. package/skeleton/storage/framework/sessions/.gitkeep +0 -0
  86. package/skeleton/storage/logs/.gitkeep +0 -0
  87. package/skeleton/tsconfig.json +33 -0
@@ -0,0 +1,62 @@
1
+ export function validateIdentifier(identifier) {
2
+ if (typeof identifier !== 'string' || identifier.length === 0) {
3
+ throw new Error('Identifier must be a non-empty string');
4
+ }
5
+
6
+ if (identifier.length > 64) {
7
+ throw new Error(`Invalid identifier: "${identifier}". Maximum length is 64 characters.`);
8
+ }
9
+
10
+ if (identifier === '*' || identifier === '*.*') {
11
+ return identifier;
12
+ }
13
+
14
+ if (/--|#|\/\*|\*\//.test(identifier)) {
15
+ throw new Error(`Invalid identifier: "${identifier}". SQL comments are not allowed.`);
16
+ }
17
+
18
+ const patterns = [
19
+ /^[a-zA-Z_][a-zA-Z0-9_]*$/,
20
+ /^[a-zA-Z_][a-zA-Z0-9_]*\.[a-zA-Z_][a-zA-Z0-9_]*$/,
21
+ /^[a-zA-Z_][a-zA-Z0-9_]*\.\*$/
22
+ ];
23
+
24
+ const isValid = patterns.some(pattern => pattern.test(identifier));
25
+
26
+ if (!isValid) {
27
+ throw new Error(`Invalid identifier: "${identifier}". Must be alphanumeric with underscores, optionally qualified with table name.`);
28
+ }
29
+
30
+ return identifier;
31
+ }
32
+
33
+ export function validateWhereOperator(operator) {
34
+ const normalized = String(operator).trim().toUpperCase();
35
+ const allowedOperators = new Set([
36
+ '=', '!=', '<>', '<', '>', '<=', '>=',
37
+ 'LIKE', 'NOT LIKE', 'ILIKE', 'NOT ILIKE',
38
+ 'IN', 'NOT IN',
39
+ 'IS', 'IS NOT',
40
+ 'BETWEEN', 'NOT BETWEEN',
41
+ 'REGEXP', 'NOT REGEXP',
42
+ 'RLIKE', 'NOT RLIKE'
43
+ ]);
44
+
45
+ if (!allowedOperators.has(normalized)) {
46
+ throw new Error(
47
+ `Invalid WHERE operator: '${operator}'. ` +
48
+ `Allowed operators: ${[...allowedOperators].join(', ')}`
49
+ );
50
+ }
51
+
52
+ return normalized;
53
+ }
54
+
55
+ export function validateDirection(direction) {
56
+ const upper = String(direction).toUpperCase();
57
+ if (upper !== 'ASC' && upper !== 'DESC') {
58
+ throw new Error(`Invalid direction: ${direction}. Use 'ASC' or 'DESC'.`);
59
+ }
60
+
61
+ return upper;
62
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Blueprint - Table schema builder
3
+ * Laravel-style fluent API for defining database tables
4
+ */
5
+ class Blueprint {
6
+ #tableName;
7
+ #columns = [];
8
+
9
+ constructor(tableName) {
10
+ this.#tableName = tableName;
11
+ }
12
+
13
+ /**
14
+ * Get table name
15
+ */
16
+ getTableName() {
17
+ return this.#tableName;
18
+ }
19
+
20
+ /**
21
+ * Get all columns
22
+ */
23
+ getColumns() {
24
+ return this.#columns;
25
+ }
26
+
27
+ /**
28
+ * Add a column definition
29
+ */
30
+ #addColumn(type, name, options = {}) {
31
+ const column = {
32
+ name,
33
+ type,
34
+ ...options,
35
+ modifiers: {
36
+ nullable: false,
37
+ default: null,
38
+ unique: false
39
+ }
40
+ };
41
+
42
+ this.#columns.push(column);
43
+
44
+ // Return modifier object for chaining
45
+ return {
46
+ nullable: () => {
47
+ column.modifiers.nullable = true;
48
+ return this;
49
+ },
50
+ default: (value) => {
51
+ column.modifiers.default = value;
52
+ return this;
53
+ },
54
+ unique: () => {
55
+ column.modifiers.unique = true;
56
+ return this;
57
+ }
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Primary key: BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY
63
+ */
64
+ id() {
65
+ const column = {
66
+ name: 'id',
67
+ type: 'id',
68
+ modifiers: {}
69
+ };
70
+ this.#columns.push(column);
71
+
72
+ // No chaining for id()
73
+ return this;
74
+ }
75
+
76
+ /**
77
+ * VARCHAR column
78
+ */
79
+ string(name, length = 255) {
80
+ return this.#addColumn('string', name, { length });
81
+ }
82
+
83
+ /**
84
+ * TEXT column
85
+ */
86
+ text(name) {
87
+ return this.#addColumn('text', name);
88
+ }
89
+
90
+ /**
91
+ * INT column
92
+ */
93
+ integer(name) {
94
+ return this.#addColumn('integer', name);
95
+ }
96
+
97
+ /**
98
+ * BIGINT column
99
+ */
100
+ bigInteger(name) {
101
+ return this.#addColumn('bigInteger', name);
102
+ }
103
+
104
+ /**
105
+ * TINYINT(1) column for boolean values
106
+ */
107
+ boolean(name) {
108
+ return this.#addColumn('boolean', name);
109
+ }
110
+
111
+ /**
112
+ * TIMESTAMP column
113
+ */
114
+ timestamp(name) {
115
+ return this.#addColumn('timestamp', name);
116
+ }
117
+
118
+ /**
119
+ * JSON column
120
+ */
121
+ json(name) {
122
+ return this.#addColumn('json', name);
123
+ }
124
+ }
125
+
126
+ export default Blueprint;
@@ -0,0 +1,116 @@
1
+ import DatabaseManager from "../Manager.js";
2
+ import Config from "../../Core/Config.js";
3
+
4
+ /**
5
+ * Schema Builder
6
+ * Laravel-style database schema operations
7
+ */
8
+ export default class Schema {
9
+ /**
10
+ * Create a new table
11
+ */
12
+ static async create(tableName, callback) {
13
+ const blueprint = new (await import('./Blueprint.js')).default(tableName);
14
+ callback(blueprint);
15
+
16
+ const sql = this.#buildCreateTableSQL(blueprint);
17
+
18
+ const connection = DatabaseManager.getInstance().connection();
19
+ await connection.raw(sql);
20
+ }
21
+
22
+ /**
23
+ * Drop table if exists
24
+ */
25
+ static async dropIfExists(tableName) {
26
+ const connection = DatabaseManager.getInstance().connection();
27
+ await connection.raw(`DROP TABLE IF EXISTS \`${tableName}\``);
28
+ }
29
+
30
+ /**
31
+ * Build CREATE TABLE SQL
32
+ */
33
+ static #buildCreateTableSQL(blueprint) {
34
+ const columns = blueprint.getColumns();
35
+ const columnsSql = columns.map(col => this.#buildColumnSQL(col));
36
+
37
+ // Get connection config
38
+ const manager = DatabaseManager.getInstance();
39
+ const connection = manager.connection();
40
+ const connectionName = connection.getName();
41
+ const databaseConfig = Config.all('database');
42
+ const dbConfig = databaseConfig.connections[connectionName];
43
+
44
+ const charset = dbConfig.charset || 'utf8mb4';
45
+ const collation = dbConfig.collation || 'utf8mb4_unicode_ci';
46
+
47
+ let sql = `CREATE TABLE \`${blueprint.getTableName()}\` (\n`;
48
+ sql += ' ' + columnsSql.join(',\n ');
49
+ sql += `\n) ENGINE=InnoDB DEFAULT CHARSET=${charset} COLLATE=${collation}`;
50
+
51
+ return sql;
52
+ }
53
+
54
+ /**
55
+ * Build SQL for a single column
56
+ */
57
+ static #buildColumnSQL(column) {
58
+ let sql = `\`${column.name}\` `;
59
+
60
+ // Type
61
+ switch (column.type) {
62
+ case 'id':
63
+ // id is special - no modifiers
64
+ sql += 'BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY';
65
+ return sql;
66
+ case 'string':
67
+ sql += `VARCHAR(${column.length || 255})`;
68
+ break;
69
+ case 'text':
70
+ sql += 'TEXT';
71
+ break;
72
+ case 'integer':
73
+ sql += 'INT';
74
+ break;
75
+ case 'bigInteger':
76
+ sql += 'BIGINT';
77
+ break;
78
+ case 'boolean':
79
+ sql += 'TINYINT(1)';
80
+ break;
81
+ case 'timestamp':
82
+ sql += 'TIMESTAMP';
83
+ break;
84
+ case 'json':
85
+ sql += 'JSON';
86
+ break;
87
+ default:
88
+ throw new Error(`Unknown column type: ${column.type}`);
89
+ }
90
+
91
+ // Modifiers (not for id)
92
+ if (column.modifiers) {
93
+ // NOT NULL / NULL
94
+ if (column.modifiers.nullable) {
95
+ sql += ' NULL';
96
+ } else {
97
+ sql += ' NOT NULL';
98
+ }
99
+
100
+ // DEFAULT
101
+ if (column.modifiers.default !== null) {
102
+ const defaultValue = typeof column.modifiers.default === 'string'
103
+ ? `'${column.modifiers.default}'`
104
+ : column.modifiers.default;
105
+ sql += ` DEFAULT ${defaultValue}`;
106
+ }
107
+
108
+ // UNIQUE
109
+ if (column.modifiers.unique) {
110
+ sql += ' UNIQUE';
111
+ }
112
+ }
113
+
114
+ return sql;
115
+ }
116
+ }
@@ -0,0 +1,108 @@
1
+ import locale from './Locale.js';
2
+ import Config from '../Core/Config.js';
3
+
4
+ class DateTime {
5
+ static #getDate(date = null) {
6
+ const timezone = Config.get('app.timezone', 'UTC');
7
+ const d = date ? new Date(date) : new Date();
8
+
9
+ return new Date(d.toLocaleString('en-US', { timeZone: timezone }));
10
+ }
11
+
12
+ static #format(date, formatString) {
13
+ const lang = locale[Config.get('app.locale', 'en')] || locale.en;
14
+ const pad = (n) => String(n).padStart(2, '0');
15
+
16
+ const map = {
17
+ 'Y': date.getFullYear(),
18
+ 'y': String(date.getFullYear()).slice(-2),
19
+ 'm': pad(date.getMonth() + 1),
20
+ 'n': date.getMonth() + 1,
21
+ 'd': pad(date.getDate()),
22
+ 'j': date.getDate(),
23
+ 'H': pad(date.getHours()),
24
+ 'i': pad(date.getMinutes()),
25
+ 's': pad(date.getSeconds()),
26
+ 'D': lang.days[date.getDay()],
27
+ 'l': lang.daysShort[date.getDay()],
28
+ 'M': lang.months[date.getMonth()],
29
+ 'F': lang.monthsShort[date.getMonth()]
30
+ };
31
+
32
+ return formatString.replace(/[YynmdjHislDMF]/g, match => map[match]);
33
+ }
34
+
35
+ /**
36
+ * Get current date/time in SQL format
37
+ * @param {number|null} timestamp - Optional timestamp
38
+ * @returns {string} SQL formatted datetime (YYYY-MM-DD HH:mm:ss)
39
+ */
40
+ static toSQL(timestamp = null) {
41
+ const d = timestamp ? new Date(timestamp) : this.#getDate();
42
+ return d.toISOString().slice(0, 19).replace('T', ' ');
43
+ }
44
+
45
+ /**
46
+ * Get timestamp in milliseconds
47
+ * @param {string|null} sqlDateTime - Optional SQL datetime string
48
+ * @returns {number} Timestamp in milliseconds
49
+ */
50
+ static getTime(sqlDateTime = null) {
51
+ if (sqlDateTime === null) {
52
+ return this.#getDate().getTime();
53
+ }
54
+ return new Date(sqlDateTime).getTime();
55
+ }
56
+
57
+ /**
58
+ * Get formatted date
59
+ * @param {number|null} timestamp - Optional timestamp
60
+ * @param {string} format - Date format string
61
+ * @returns {string} Formatted date
62
+ */
63
+ static getDate(timestamp = null, format = 'Y-m-d H:i:s') {
64
+ return this.#format(this.#getDate(timestamp), format);
65
+ }
66
+
67
+ /**
68
+ * Add days to current date
69
+ * @param {number} days - Number of days to add
70
+ * @returns {string} SQL formatted datetime
71
+ */
72
+ static addDays(days) {
73
+ const date = this.#getDate();
74
+ date.setDate(date.getDate() + days);
75
+ return date.toISOString().slice(0, 19).replace('T', ' ');
76
+ }
77
+
78
+ /**
79
+ * Add hours to current date
80
+ * @param {number} hours - Number of hours to add
81
+ * @returns {string} SQL formatted datetime
82
+ */
83
+ static addHours(hours) {
84
+ const date = this.#getDate();
85
+ date.setHours(date.getHours() + hours);
86
+ return date.toISOString().slice(0, 19).replace('T', ' ');
87
+ }
88
+
89
+ /**
90
+ * Subtract days from current date
91
+ * @param {number} days - Number of days to subtract
92
+ * @returns {string} SQL formatted datetime
93
+ */
94
+ static subDays(days) {
95
+ return this.addDays(-days);
96
+ }
97
+
98
+ /**
99
+ * Subtract hours from current date
100
+ * @param {number} hours - Number of hours to subtract
101
+ * @returns {string} SQL formatted datetime
102
+ */
103
+ static subHours(hours) {
104
+ return this.addHours(-hours);
105
+ }
106
+ }
107
+
108
+ export default DateTime;
@@ -0,0 +1,68 @@
1
+ export default {
2
+ tr: {
3
+ days: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'],
4
+ daysShort: ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
5
+ months: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
6
+ monthsShort: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara']
7
+ },
8
+ en: {
9
+ days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
10
+ daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
11
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
12
+ monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
13
+ },
14
+ de: {
15
+ days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
16
+ daysShort: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
17
+ months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
18
+ monthsShort: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']
19
+ },
20
+ fr: {
21
+ days: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
22
+ daysShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
23
+ months: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
24
+ monthsShort: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun', 'Jul', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc']
25
+ },
26
+ es: {
27
+ days: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
28
+ daysShort: ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'],
29
+ months: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
30
+ monthsShort: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic']
31
+ },
32
+ it: {
33
+ days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'],
34
+ daysShort: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'],
35
+ months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
36
+ monthsShort: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic']
37
+ },
38
+ pt: {
39
+ days: ['Domingo', 'Segunda-feira', 'Terça-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'Sábado'],
40
+ daysShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
41
+ months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
42
+ monthsShort: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']
43
+ },
44
+ ru: {
45
+ days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
46
+ daysShort: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
47
+ months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
48
+ monthsShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек']
49
+ },
50
+ ar: {
51
+ days: ['الأحد', 'الإثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
52
+ daysShort: ['أحد', 'إثن', 'ثلا', 'أرب', 'خمي', 'جمع', 'سبت'],
53
+ months: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
54
+ monthsShort: ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس']
55
+ },
56
+ ja: {
57
+ days: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'],
58
+ daysShort: ['日', '月', '火', '水', '木', '金', '土'],
59
+ months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
60
+ monthsShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
61
+ },
62
+ zh: {
63
+ days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
64
+ daysShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
65
+ months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
66
+ monthsShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
67
+ }
68
+ };
@@ -0,0 +1,47 @@
1
+ import crypto from "crypto";
2
+
3
+ class EncryptionManager {
4
+ static encrypt(value) {
5
+ if (typeof value === "object") {
6
+ value = JSON.stringify(value);
7
+ }
8
+
9
+ const secretKey = this.#createSecretKey();
10
+ const iv = this.#createIV();
11
+
12
+ const cipher = crypto.createCipheriv("aes-256-cbc", secretKey, iv);
13
+ let encrypted = cipher.update(value, "utf8", "hex");
14
+ encrypted += cipher.final("hex");
15
+ encrypted = iv.toString("hex") + ":" + encrypted;
16
+
17
+ return encrypted;
18
+ }
19
+
20
+ static decrypt(encryptedValue) {
21
+ try {
22
+ const parts = encryptedValue.split(":");
23
+ const iv = Buffer.from(parts.shift(), "hex");
24
+ const encryptedText = Buffer.from(parts.join(":"), "hex");
25
+
26
+ const secretKey = this.#createSecretKey();
27
+
28
+ const decipher = crypto.createDecipheriv("aes-256-cbc", secretKey, iv);
29
+ let decrypted = decipher.update(encryptedText, "hex", "utf8");
30
+ decrypted += decipher.final("utf8");
31
+
32
+ return decrypted;
33
+ } catch (error) {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ static #createSecretKey() {
39
+ return crypto.createHash("sha256").update(process.env.APP_KEY).digest();
40
+ }
41
+
42
+ static #createIV() {
43
+ return crypto.randomBytes(16);
44
+ }
45
+ }
46
+
47
+ export default EncryptionManager;
@@ -0,0 +1,49 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import Paths from "../Core/Paths.js";
4
+
5
+ class FilesystemManager {
6
+ static #publicRoot = Paths.storagePublic;
7
+ static #privateRoot = Paths.storagePrivate;
8
+
9
+ static async get(filePath, isPrivate = false) {
10
+ const base = isPrivate ? this.#privateRoot : this.#publicRoot;
11
+ const fullPath = path.join(base, filePath);
12
+
13
+ try {
14
+ return await fs.promises.readFile(fullPath);
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+
20
+ static async put(file, dir, fileName, isPrivate = false) {
21
+ const base = isPrivate ? this.#privateRoot : this.#publicRoot;
22
+ const folderPath = path.join(base, dir);
23
+ const fullPath = path.join(folderPath, fileName);
24
+
25
+ await fs.promises.mkdir(folderPath, { recursive: true });
26
+ await fs.promises.writeFile(fullPath, file._buf);
27
+
28
+ return true;
29
+ }
30
+
31
+ static async delete(filePath, isPrivate = false) {
32
+ const base = isPrivate ? this.#privateRoot : this.#publicRoot;
33
+ await fs.promises.unlink(path.join(base, filePath));
34
+ }
35
+
36
+ static exists(filePath, isPrivate = false) {
37
+ const base = isPrivate ? this.#privateRoot : this.#publicRoot;
38
+ return fs.existsSync(path.join(base, filePath));
39
+ }
40
+
41
+ static url(filePath) {
42
+ if (filePath.startsWith("/")) {
43
+ filePath = filePath.substring(1);
44
+ }
45
+ return `/storage/${filePath}`;
46
+ }
47
+ }
48
+
49
+ export default FilesystemManager;
@@ -0,0 +1,25 @@
1
+ import bcrypt from "bcrypt";
2
+ import Config from "../Core/Config.js";
3
+
4
+ class HashManager {
5
+ static async make(textField) {
6
+ if (!process.env.APP_KEY) {
7
+ throw new Error("APP_KEY is required for hashing");
8
+ }
9
+
10
+ const saltRounds = Config.get("hash.salt_rounds", 10);
11
+ const salt = await bcrypt.genSalt(saltRounds);
12
+
13
+ return await bcrypt.hash(textField + process.env.APP_KEY, salt);
14
+ }
15
+
16
+ static async check(textField, hashedText) {
17
+ if (!process.env.APP_KEY) {
18
+ throw new Error("APP_KEY is required for hashing");
19
+ }
20
+
21
+ return await bcrypt.compare(textField + process.env.APP_KEY, hashedText);
22
+ }
23
+ }
24
+
25
+ export default HashManager;