@fuzionx/framework 0.1.42 → 0.1.44

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 (80) hide show
  1. package/README.md +501 -501
  2. package/bin/fx.js +12 -12
  3. package/cli/db-sync.js +100 -100
  4. package/cli/index.js +494 -494
  5. package/cli/templates/make/app/controllers/HomeController.js +14 -14
  6. package/cli/templates/make/app/routes/api.js +7 -7
  7. package/cli/templates/make/app/routes/web.js +5 -5
  8. package/cli/templates/make/app/views/default/errors/404.html +11 -11
  9. package/cli/templates/make/app/views/default/errors/500.html +14 -14
  10. package/cli/templates/make/app/views/default/layouts/main.html +22 -22
  11. package/cli/templates/make/app/views/default/pages/home.html +11 -11
  12. package/cli/templates/make/controller.js.tpl +40 -40
  13. package/cli/templates/make/event.js.tpl +8 -8
  14. package/cli/templates/make/job.js.tpl +10 -10
  15. package/cli/templates/make/middleware.js.tpl +10 -10
  16. package/cli/templates/make/model.js.tpl +15 -15
  17. package/cli/templates/make/service.js.tpl +15 -15
  18. package/cli/templates/make/task.js.tpl +15 -15
  19. package/cli/templates/make/test.js.tpl +7 -7
  20. package/cli/templates/make/worker.js.tpl +14 -14
  21. package/cli/templates/make/ws.js.tpl +18 -18
  22. package/index.js +67 -67
  23. package/lib/core/AppError.js +46 -46
  24. package/lib/core/Application.js +1006 -1006
  25. package/lib/core/AutoLoader.js +227 -227
  26. package/lib/core/Base.js +64 -64
  27. package/lib/core/Config.js +331 -228
  28. package/lib/core/Context.js +484 -484
  29. package/lib/database/ConnectionManager.js +208 -208
  30. package/lib/database/MariaModel.js +29 -29
  31. package/lib/database/Model.js +247 -247
  32. package/lib/database/ModelRegistry.js +72 -72
  33. package/lib/database/MongoModel.js +232 -232
  34. package/lib/database/Pagination.js +37 -37
  35. package/lib/database/PostgreModel.js +29 -29
  36. package/lib/database/QueryBuilder.js +172 -172
  37. package/lib/database/SQLiteModel.js +27 -27
  38. package/lib/database/SqlModel.js +257 -257
  39. package/lib/database/SqlQueryBuilder.js +332 -332
  40. package/lib/helpers/CryptoHelper.js +48 -48
  41. package/lib/helpers/FileHelper.js +61 -61
  42. package/lib/helpers/HashHelper.js +39 -39
  43. package/lib/helpers/I18nHelper.js +174 -174
  44. package/lib/helpers/Logger.js +108 -108
  45. package/lib/helpers/MediaHelper.js +84 -84
  46. package/lib/http/Controller.js +34 -34
  47. package/lib/http/ErrorHandler.js +136 -136
  48. package/lib/http/Middleware.js +43 -43
  49. package/lib/http/Router.js +109 -109
  50. package/lib/http/Validation.js +125 -125
  51. package/lib/middleware/apiAuth.js +79 -79
  52. package/lib/middleware/auth.js +42 -42
  53. package/lib/middleware/bodyParser.js +19 -19
  54. package/lib/middleware/cors.js +47 -47
  55. package/lib/middleware/csrf.js +32 -32
  56. package/lib/middleware/index.js +13 -13
  57. package/lib/middleware/session.js +27 -27
  58. package/lib/middleware/theme.js +20 -20
  59. package/lib/realtime/RoomManager.js +85 -85
  60. package/lib/realtime/WsHandler.js +107 -107
  61. package/lib/schedule/Job.js +38 -38
  62. package/lib/schedule/Queue.js +103 -103
  63. package/lib/schedule/Scheduler.js +171 -171
  64. package/lib/schedule/Task.js +39 -39
  65. package/lib/schedule/WorkerPool.js +225 -225
  66. package/lib/services/EventBus.js +94 -94
  67. package/lib/services/Service.js +261 -261
  68. package/lib/services/Storage.js +112 -112
  69. package/lib/utilities/ArrUtil.js +112 -112
  70. package/lib/utilities/DateUtil.js +98 -98
  71. package/lib/utilities/FunctionUtil.js +119 -119
  72. package/lib/utilities/NumUtil.js +75 -75
  73. package/lib/utilities/ObjectUtil.js +170 -170
  74. package/lib/utilities/PaginationUtil.js +81 -81
  75. package/lib/utilities/StrUtil.js +105 -105
  76. package/lib/utilities/index.js +18 -18
  77. package/lib/view/OpenAPI.js +231 -231
  78. package/lib/view/View.js +83 -83
  79. package/package.json +2 -2
  80. package/testing/index.js +232 -232
@@ -1,232 +1,232 @@
1
- /**
2
- * MongoModel — MongoDB 모델 서브클래스
3
- *
4
- * Mongoose 기반. SqlModel과 별도 계층.
5
- * MongoDB 전용 API (aggregate, populate 등)를 별도 구현.
6
- *
7
- * @see docs/framework/02-database-orm.md
8
- *
9
- * @example
10
- * import { MongoModel } from '@fuzionx/framework';
11
- *
12
- * export default class AccessLog extends MongoModel {
13
- * static table = 'access_logs';
14
- * static connection = 'mongo';
15
- * static timestamps = true;
16
- * static schema = {
17
- * level: { type: 'string', required: true },
18
- * message: { type: 'string' },
19
- * meta: { type: 'object' },
20
- * };
21
- * }
22
- */
23
- import Model from './Model.js';
24
- import QueryBuilder from './QueryBuilder.js';
25
-
26
- export default class MongoModel extends Model {
27
- static driver = 'mongodb';
28
- static connection = 'mongo';
29
-
30
- /** @type {import('./ConnectionManager.js').default|null} */
31
- static _connectionManager = null;
32
-
33
- /**
34
- * ConnectionManager를 MongoModel에 주입
35
- * @param {import('./ConnectionManager.js').default} cm
36
- */
37
- static setConnectionManager(cm) {
38
- MongoModel._connectionManager = cm;
39
- }
40
-
41
- /**
42
- * DB 연결 가져오기
43
- * @returns {object}
44
- */
45
- static getConnection() {
46
- if (!MongoModel._connectionManager) {
47
- throw new Error('DB not initialized. Ensure Application.boot() has been called.');
48
- }
49
- return MongoModel._connectionManager.get(this.connection);
50
- }
51
-
52
- /**
53
- * Mongoose 모델 가져오기 (lazy 생성)
54
- * @returns {import('mongoose').Model}
55
- */
56
- static _getMongooseModel() {
57
- if (this._mongooseModel) return this._mongooseModel;
58
-
59
- const conn = this.getConnection();
60
- if (!conn.mongoose) {
61
- throw new Error('MongoDB requires mongoose. Install: npm install mongoose');
62
- }
63
-
64
- // schema 정의 → Mongoose 모델 생성
65
- const schemaFields = {};
66
- for (const [key, def] of Object.entries(this.schema || {})) {
67
- const mongoType = this._toMongooseType(def.type);
68
- schemaFields[key] = {
69
- type: mongoType,
70
- required: def.required || false,
71
- default: def.default,
72
- };
73
- }
74
-
75
- const options = { collection: this.table };
76
- if (this.timestamps) {
77
- options.timestamps = { createdAt: 'created_at', updatedAt: 'updated_at' };
78
- }
79
-
80
- const schema = new conn.mongoose.Schema(schemaFields, options);
81
- this._mongooseModel = conn.mongoose.model(this.name, schema);
82
- return this._mongooseModel;
83
- }
84
-
85
- /**
86
- * FuzionX 타입 → Mongoose 타입 변환
87
- * @private
88
- */
89
- static _toMongooseType(type) {
90
- const mongoose = this.getConnection().mongoose;
91
- if (!mongoose) return String;
92
-
93
- const map = {
94
- 'string': String,
95
- 'integer': Number,
96
- 'bigInteger': Number,
97
- 'float': Number,
98
- 'decimal': Number,
99
- 'boolean': Boolean,
100
- 'date': Date,
101
- 'datetime': Date,
102
- 'timestamp': Date,
103
- 'object': Object,
104
- 'json': Object,
105
- 'array': Array,
106
- 'binary': Buffer,
107
- };
108
- return map[type] || String;
109
- }
110
-
111
- /**
112
- * 레코드 생성
113
- * @param {object} data
114
- * @returns {Promise<MongoModel>}
115
- */
116
- static async create(data) {
117
- try {
118
- const MongooseModel = this._getMongooseModel();
119
- const doc = await MongooseModel.create(data);
120
- return new this(doc.toObject());
121
- } catch {
122
- // Mongoose 미설치 → stub
123
- return new this({ ...data, _id: Date.now().toString(36) });
124
- }
125
- }
126
-
127
- /**
128
- * ID로 조회
129
- * @param {*} id
130
- * @returns {Promise<MongoModel|null>}
131
- */
132
- static async find(id) {
133
- try {
134
- const MongooseModel = this._getMongooseModel();
135
- const doc = await MongooseModel.findById(id);
136
- return doc ? new this(doc.toObject()) : null;
137
- } catch {
138
- return null;
139
- }
140
- }
141
-
142
- /**
143
- * 전체 조회
144
- * @returns {Promise<Array>}
145
- */
146
- static async all() {
147
- try {
148
- const MongooseModel = this._getMongooseModel();
149
- const docs = await MongooseModel.find({});
150
- return docs.map(d => new this(d.toObject()));
151
- } catch {
152
- return [];
153
- }
154
- }
155
-
156
- static async findAll() { return this.all(); }
157
-
158
- /**
159
- * Raw aggregation pipeline
160
- * @param {Array} pipeline
161
- * @returns {Promise<Array>}
162
- */
163
- static async raw(pipeline) {
164
- try {
165
- const MongooseModel = this._getMongooseModel();
166
- return MongooseModel.aggregate(pipeline);
167
- } catch {
168
- return [];
169
- }
170
- }
171
-
172
- /**
173
- * 네이티브 Mongoose 접근
174
- * @returns {object|null}
175
- */
176
- static nativeConnection() {
177
- try {
178
- return this.getConnection().mongoose;
179
- } catch {
180
- return null;
181
- }
182
- }
183
-
184
- // ━━━━━━━━━━ 인스턴스 메서드 ━━━━━━━━━━
185
-
186
- /**
187
- * 인스턴스 수정
188
- * @param {object} data
189
- * @returns {Promise<this>}
190
- */
191
- async update(data) {
192
- Object.assign(this._attributes, data);
193
- Object.assign(this, data);
194
-
195
- const id = this._attributes._id || this._attributes[this.constructor.primaryKey];
196
- if (!id) return this;
197
-
198
- try {
199
- const MongooseModel = this.constructor._getMongooseModel();
200
- await MongooseModel.updateOne({ _id: id }, { $set: data });
201
- } catch {}
202
-
203
- return this;
204
- }
205
-
206
- /**
207
- * 삭제
208
- */
209
- async delete() {
210
- if (this.constructor.softDelete) {
211
- const now = new Date().toISOString();
212
- this._attributes.deleted_at = now;
213
- this.deleted_at = now;
214
- await this.update({ deleted_at: now });
215
- return;
216
- }
217
- await this.forceDelete();
218
- }
219
-
220
- /**
221
- * 실제 삭제
222
- */
223
- async forceDelete() {
224
- const id = this._attributes._id || this._attributes[this.constructor.primaryKey];
225
- if (!id) return;
226
-
227
- try {
228
- const MongooseModel = this.constructor._getMongooseModel();
229
- await MongooseModel.deleteOne({ _id: id });
230
- } catch {}
231
- }
232
- }
1
+ /**
2
+ * MongoModel — MongoDB 모델 서브클래스
3
+ *
4
+ * Mongoose 기반. SqlModel과 별도 계층.
5
+ * MongoDB 전용 API (aggregate, populate 등)를 별도 구현.
6
+ *
7
+ * @see docs/framework/02-database-orm.md
8
+ *
9
+ * @example
10
+ * import { MongoModel } from '@fuzionx/framework';
11
+ *
12
+ * export default class AccessLog extends MongoModel {
13
+ * static table = 'access_logs';
14
+ * static connection = 'mongo';
15
+ * static timestamps = true;
16
+ * static schema = {
17
+ * level: { type: 'string', required: true },
18
+ * message: { type: 'string' },
19
+ * meta: { type: 'object' },
20
+ * };
21
+ * }
22
+ */
23
+ import Model from './Model.js';
24
+ import QueryBuilder from './QueryBuilder.js';
25
+
26
+ export default class MongoModel extends Model {
27
+ static driver = 'mongodb';
28
+ static connection = 'mongo';
29
+
30
+ /** @type {import('./ConnectionManager.js').default|null} */
31
+ static _connectionManager = null;
32
+
33
+ /**
34
+ * ConnectionManager를 MongoModel에 주입
35
+ * @param {import('./ConnectionManager.js').default} cm
36
+ */
37
+ static setConnectionManager(cm) {
38
+ MongoModel._connectionManager = cm;
39
+ }
40
+
41
+ /**
42
+ * DB 연결 가져오기
43
+ * @returns {object}
44
+ */
45
+ static getConnection() {
46
+ if (!MongoModel._connectionManager) {
47
+ throw new Error('DB not initialized. Ensure Application.boot() has been called.');
48
+ }
49
+ return MongoModel._connectionManager.get(this.connection);
50
+ }
51
+
52
+ /**
53
+ * Mongoose 모델 가져오기 (lazy 생성)
54
+ * @returns {import('mongoose').Model}
55
+ */
56
+ static _getMongooseModel() {
57
+ if (this._mongooseModel) return this._mongooseModel;
58
+
59
+ const conn = this.getConnection();
60
+ if (!conn.mongoose) {
61
+ throw new Error('MongoDB requires mongoose. Install: npm install mongoose');
62
+ }
63
+
64
+ // schema 정의 → Mongoose 모델 생성
65
+ const schemaFields = {};
66
+ for (const [key, def] of Object.entries(this.schema || {})) {
67
+ const mongoType = this._toMongooseType(def.type);
68
+ schemaFields[key] = {
69
+ type: mongoType,
70
+ required: def.required || false,
71
+ default: def.default,
72
+ };
73
+ }
74
+
75
+ const options = { collection: this.table };
76
+ if (this.timestamps) {
77
+ options.timestamps = { createdAt: 'created_at', updatedAt: 'updated_at' };
78
+ }
79
+
80
+ const schema = new conn.mongoose.Schema(schemaFields, options);
81
+ this._mongooseModel = conn.mongoose.model(this.name, schema);
82
+ return this._mongooseModel;
83
+ }
84
+
85
+ /**
86
+ * FuzionX 타입 → Mongoose 타입 변환
87
+ * @private
88
+ */
89
+ static _toMongooseType(type) {
90
+ const mongoose = this.getConnection().mongoose;
91
+ if (!mongoose) return String;
92
+
93
+ const map = {
94
+ 'string': String,
95
+ 'integer': Number,
96
+ 'bigInteger': Number,
97
+ 'float': Number,
98
+ 'decimal': Number,
99
+ 'boolean': Boolean,
100
+ 'date': Date,
101
+ 'datetime': Date,
102
+ 'timestamp': Date,
103
+ 'object': Object,
104
+ 'json': Object,
105
+ 'array': Array,
106
+ 'binary': Buffer,
107
+ };
108
+ return map[type] || String;
109
+ }
110
+
111
+ /**
112
+ * 레코드 생성
113
+ * @param {object} data
114
+ * @returns {Promise<MongoModel>}
115
+ */
116
+ static async create(data) {
117
+ try {
118
+ const MongooseModel = this._getMongooseModel();
119
+ const doc = await MongooseModel.create(data);
120
+ return new this(doc.toObject());
121
+ } catch {
122
+ // Mongoose 미설치 → stub
123
+ return new this({ ...data, _id: Date.now().toString(36) });
124
+ }
125
+ }
126
+
127
+ /**
128
+ * ID로 조회
129
+ * @param {*} id
130
+ * @returns {Promise<MongoModel|null>}
131
+ */
132
+ static async find(id) {
133
+ try {
134
+ const MongooseModel = this._getMongooseModel();
135
+ const doc = await MongooseModel.findById(id);
136
+ return doc ? new this(doc.toObject()) : null;
137
+ } catch {
138
+ return null;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * 전체 조회
144
+ * @returns {Promise<Array>}
145
+ */
146
+ static async all() {
147
+ try {
148
+ const MongooseModel = this._getMongooseModel();
149
+ const docs = await MongooseModel.find({});
150
+ return docs.map(d => new this(d.toObject()));
151
+ } catch {
152
+ return [];
153
+ }
154
+ }
155
+
156
+ static async findAll() { return this.all(); }
157
+
158
+ /**
159
+ * Raw aggregation pipeline
160
+ * @param {Array} pipeline
161
+ * @returns {Promise<Array>}
162
+ */
163
+ static async raw(pipeline) {
164
+ try {
165
+ const MongooseModel = this._getMongooseModel();
166
+ return MongooseModel.aggregate(pipeline);
167
+ } catch {
168
+ return [];
169
+ }
170
+ }
171
+
172
+ /**
173
+ * 네이티브 Mongoose 접근
174
+ * @returns {object|null}
175
+ */
176
+ static nativeConnection() {
177
+ try {
178
+ return this.getConnection().mongoose;
179
+ } catch {
180
+ return null;
181
+ }
182
+ }
183
+
184
+ // ━━━━━━━━━━ 인스턴스 메서드 ━━━━━━━━━━
185
+
186
+ /**
187
+ * 인스턴스 수정
188
+ * @param {object} data
189
+ * @returns {Promise<this>}
190
+ */
191
+ async update(data) {
192
+ Object.assign(this._attributes, data);
193
+ Object.assign(this, data);
194
+
195
+ const id = this._attributes._id || this._attributes[this.constructor.primaryKey];
196
+ if (!id) return this;
197
+
198
+ try {
199
+ const MongooseModel = this.constructor._getMongooseModel();
200
+ await MongooseModel.updateOne({ _id: id }, { $set: data });
201
+ } catch {}
202
+
203
+ return this;
204
+ }
205
+
206
+ /**
207
+ * 삭제
208
+ */
209
+ async delete() {
210
+ if (this.constructor.softDelete) {
211
+ const now = new Date().toISOString();
212
+ this._attributes.deleted_at = now;
213
+ this.deleted_at = now;
214
+ await this.update({ deleted_at: now });
215
+ return;
216
+ }
217
+ await this.forceDelete();
218
+ }
219
+
220
+ /**
221
+ * 실제 삭제
222
+ */
223
+ async forceDelete() {
224
+ const id = this._attributes._id || this._attributes[this.constructor.primaryKey];
225
+ if (!id) return;
226
+
227
+ try {
228
+ const MongooseModel = this.constructor._getMongooseModel();
229
+ await MongooseModel.deleteOne({ _id: id });
230
+ } catch {}
231
+ }
232
+ }
@@ -1,37 +1,37 @@
1
- /**
2
- * Pagination — 페이지네이션 결과 객체
3
- *
4
- * @see docs/framework/class-design.mm.md (Pagination)
5
- */
6
- export default class Pagination {
7
- /**
8
- * @param {Array} data - 현재 페이지 데이터
9
- * @param {number} total - 전체 레코드 수
10
- * @param {number} page - 현재 페이지
11
- * @param {number} perPage - 페이지당 수
12
- */
13
- constructor(data, total, page, perPage) {
14
- this.data = data;
15
- this.total = total;
16
- this.page = page;
17
- this.perPage = perPage;
18
- this.lastPage = Math.ceil(total / perPage) || 1;
19
- this.hasMore = page < this.lastPage;
20
- }
21
-
22
- /**
23
- * JSON 직렬화 형태
24
- */
25
- toJSON() {
26
- return {
27
- data: this.data,
28
- meta: {
29
- total: this.total,
30
- page: this.page,
31
- perPage: this.perPage,
32
- lastPage: this.lastPage,
33
- hasMore: this.hasMore,
34
- },
35
- };
36
- }
37
- }
1
+ /**
2
+ * Pagination — 페이지네이션 결과 객체
3
+ *
4
+ * @see docs/framework/class-design.mm.md (Pagination)
5
+ */
6
+ export default class Pagination {
7
+ /**
8
+ * @param {Array} data - 현재 페이지 데이터
9
+ * @param {number} total - 전체 레코드 수
10
+ * @param {number} page - 현재 페이지
11
+ * @param {number} perPage - 페이지당 수
12
+ */
13
+ constructor(data, total, page, perPage) {
14
+ this.data = data;
15
+ this.total = total;
16
+ this.page = page;
17
+ this.perPage = perPage;
18
+ this.lastPage = Math.ceil(total / perPage) || 1;
19
+ this.hasMore = page < this.lastPage;
20
+ }
21
+
22
+ /**
23
+ * JSON 직렬화 형태
24
+ */
25
+ toJSON() {
26
+ return {
27
+ data: this.data,
28
+ meta: {
29
+ total: this.total,
30
+ page: this.page,
31
+ perPage: this.perPage,
32
+ lastPage: this.lastPage,
33
+ hasMore: this.hasMore,
34
+ },
35
+ };
36
+ }
37
+ }
@@ -1,29 +1,29 @@
1
- /**
2
- * PostgreModel — PostgreSQL 모델 서브클래스
3
- *
4
- * Knex + pg 기반.
5
- * SqlModel의 공통 SQL 로직을 상속하며,
6
- * PostgreSQL 고유 기능(JSONB, ARRAY, CTE 등)은 raw()로 접근.
7
- *
8
- * @see docs/framework/02-database-orm.md
9
- *
10
- * @example
11
- * import { PostgreModel } from '@fuzionx/framework';
12
- *
13
- * export default class Analytics extends PostgreModel {
14
- * static table = 'analytics';
15
- * static connection = 'analytics_db';
16
- * static columns = {
17
- * id: { type: 'increments' },
18
- * event: { type: 'string', length: 100 },
19
- * meta: { type: 'json' },
20
- * created: { type: 'timestamp' },
21
- * };
22
- * }
23
- */
24
- import SqlModel from './SqlModel.js';
25
-
26
- export default class PostgreModel extends SqlModel {
27
- static driver = 'postgres';
28
- static connection = 'main';
29
- }
1
+ /**
2
+ * PostgreModel — PostgreSQL 모델 서브클래스
3
+ *
4
+ * Knex + pg 기반.
5
+ * SqlModel의 공통 SQL 로직을 상속하며,
6
+ * PostgreSQL 고유 기능(JSONB, ARRAY, CTE 등)은 raw()로 접근.
7
+ *
8
+ * @see docs/framework/02-database-orm.md
9
+ *
10
+ * @example
11
+ * import { PostgreModel } from '@fuzionx/framework';
12
+ *
13
+ * export default class Analytics extends PostgreModel {
14
+ * static table = 'analytics';
15
+ * static connection = 'analytics_db';
16
+ * static columns = {
17
+ * id: { type: 'increments' },
18
+ * event: { type: 'string', length: 100 },
19
+ * meta: { type: 'json' },
20
+ * created: { type: 'timestamp' },
21
+ * };
22
+ * }
23
+ */
24
+ import SqlModel from './SqlModel.js';
25
+
26
+ export default class PostgreModel extends SqlModel {
27
+ static driver = 'postgres';
28
+ static connection = 'main';
29
+ }