@fuzionx/framework 0.1.38 → 0.1.41
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/README.md +501 -501
- package/bin/fx.js +12 -12
- package/cli/db-sync.js +99 -99
- package/cli/index.js +493 -493
- package/cli/templates/make/app/controllers/HomeController.js +14 -14
- package/cli/templates/make/app/routes/api.js +7 -7
- package/cli/templates/make/app/routes/web.js +5 -5
- package/cli/templates/make/app/views/default/errors/404.html +11 -11
- package/cli/templates/make/app/views/default/errors/500.html +14 -14
- package/cli/templates/make/app/views/default/layouts/main.html +22 -22
- package/cli/templates/make/app/views/default/pages/home.html +11 -11
- package/cli/templates/make/controller.js.tpl +40 -40
- package/cli/templates/make/event.js.tpl +8 -8
- package/cli/templates/make/job.js.tpl +10 -10
- package/cli/templates/make/middleware.js.tpl +10 -10
- package/cli/templates/make/model.js.tpl +15 -15
- package/cli/templates/make/service.js.tpl +15 -15
- package/cli/templates/make/task.js.tpl +15 -15
- package/cli/templates/make/test.js.tpl +7 -7
- package/cli/templates/make/worker.js.tpl +14 -14
- package/cli/templates/make/ws.js.tpl +18 -18
- package/index.js +67 -67
- package/lib/core/AppError.js +46 -46
- package/lib/core/Application.js +1006 -998
- package/lib/core/AutoLoader.js +226 -226
- package/lib/core/Base.js +64 -64
- package/lib/core/Config.js +228 -228
- package/lib/core/Context.js +484 -460
- package/lib/database/ConnectionManager.js +208 -208
- package/lib/database/MariaModel.js +29 -29
- package/lib/database/Model.js +247 -247
- package/lib/database/ModelRegistry.js +72 -72
- package/lib/database/MongoModel.js +232 -232
- package/lib/database/Pagination.js +37 -37
- package/lib/database/PostgreModel.js +29 -29
- package/lib/database/QueryBuilder.js +172 -172
- package/lib/database/SQLiteModel.js +27 -27
- package/lib/database/SqlModel.js +257 -257
- package/lib/database/SqlQueryBuilder.js +332 -321
- package/lib/helpers/CryptoHelper.js +48 -48
- package/lib/helpers/FileHelper.js +61 -61
- package/lib/helpers/HashHelper.js +39 -39
- package/lib/helpers/I18nHelper.js +174 -174
- package/lib/helpers/Logger.js +108 -105
- package/lib/helpers/MediaHelper.js +84 -84
- package/lib/http/Controller.js +34 -34
- package/lib/http/ErrorHandler.js +136 -136
- package/lib/http/Middleware.js +43 -43
- package/lib/http/Router.js +109 -109
- package/lib/http/Validation.js +125 -124
- package/lib/middleware/apiAuth.js +79 -79
- package/lib/middleware/auth.js +42 -42
- package/lib/middleware/bodyParser.js +19 -19
- package/lib/middleware/cors.js +47 -47
- package/lib/middleware/csrf.js +32 -32
- package/lib/middleware/index.js +13 -13
- package/lib/middleware/session.js +27 -27
- package/lib/middleware/theme.js +20 -20
- package/lib/realtime/RoomManager.js +85 -85
- package/lib/realtime/WsHandler.js +107 -107
- package/lib/schedule/Job.js +38 -38
- package/lib/schedule/Queue.js +103 -102
- package/lib/schedule/Scheduler.js +171 -170
- package/lib/schedule/Task.js +39 -39
- package/lib/schedule/WorkerPool.js +225 -225
- package/lib/services/EventBus.js +94 -94
- package/lib/services/Service.js +261 -261
- package/lib/services/Storage.js +112 -112
- package/lib/utilities/ArrUtil.js +112 -112
- package/lib/utilities/DateUtil.js +98 -98
- package/lib/utilities/FunctionUtil.js +119 -119
- package/lib/utilities/NumUtil.js +75 -75
- package/lib/utilities/ObjectUtil.js +170 -170
- package/lib/utilities/PaginationUtil.js +81 -81
- package/lib/utilities/StrUtil.js +105 -105
- package/lib/utilities/index.js +18 -18
- package/lib/view/OpenAPI.js +231 -231
- package/lib/view/View.js +83 -83
- package/package.json +2 -2
- package/testing/index.js +232 -232
- package/cli/fx.js +0 -3
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* QueryBuilder — SQL/MongoDB 쿼리 빌더 (체이닝)
|
|
3
|
-
*
|
|
4
|
-
* @see docs/framework/02-database-orm.md
|
|
5
|
-
* @see docs/framework/class-design.mm.md (QueryBuilder)
|
|
6
|
-
*/
|
|
7
|
-
export default class QueryBuilder {
|
|
8
|
-
constructor(model) {
|
|
9
|
-
this._model = model;
|
|
10
|
-
this._wheres = [];
|
|
11
|
-
this._orders = [];
|
|
12
|
-
this._limit = null;
|
|
13
|
-
this._offset = null;
|
|
14
|
-
this._selects = null;
|
|
15
|
-
this._withs = [];
|
|
16
|
-
// softDelete 자동 적용
|
|
17
|
-
this._softDelete = model?.softDelete || false;
|
|
18
|
-
this._withTrashed = false;
|
|
19
|
-
this._onlyTrashed = false;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
23
|
-
// 체이닝 메서드
|
|
24
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
25
|
-
|
|
26
|
-
where(key, op, value) {
|
|
27
|
-
if (value === undefined) { value = op; op = '='; }
|
|
28
|
-
this._wheres.push({ key, op, value, type: 'and' });
|
|
29
|
-
return this;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** OR 조건 */
|
|
33
|
-
orWhere(key, op, value) {
|
|
34
|
-
if (value === undefined) { value = op; op = '='; }
|
|
35
|
-
this._wheres.push({ key, op, value, type: 'or' });
|
|
36
|
-
return this;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** IN 조건 */
|
|
40
|
-
whereIn(key, values) {
|
|
41
|
-
this._wheres.push({ key, op: 'IN', value: values, type: 'and' });
|
|
42
|
-
return this;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** NOT NULL 조건 */
|
|
46
|
-
whereNotNull(key) {
|
|
47
|
-
this._wheres.push({ key, op: 'IS NOT NULL', value: null, type: 'and' });
|
|
48
|
-
return this;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** NULL 조건 */
|
|
52
|
-
whereNull(key) {
|
|
53
|
-
this._wheres.push({ key, op: 'IS NULL', value: null, type: 'and' });
|
|
54
|
-
return this;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
orderBy(column, direction = 'asc') {
|
|
58
|
-
this._orders.push({ column, direction });
|
|
59
|
-
return this;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
limit(n) { this._limit = n; return this; }
|
|
63
|
-
offset(n) { this._offset = n; return this; }
|
|
64
|
-
|
|
65
|
-
select(...columns) {
|
|
66
|
-
this._selects = columns.flat();
|
|
67
|
-
return this;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
with(...relations) {
|
|
71
|
-
this._withs.push(...relations.flat());
|
|
72
|
-
return this;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** softDelete 포함 (삭제된 레코드도 조회) */
|
|
76
|
-
withTrashed() {
|
|
77
|
-
this._withTrashed = true;
|
|
78
|
-
return this;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** softDelete만 조회 (삭제된 레코드만) */
|
|
82
|
-
onlyTrashed() {
|
|
83
|
-
this._onlyTrashed = true;
|
|
84
|
-
return this;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
88
|
-
// 터미널 메서드
|
|
89
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* 페이지네이션
|
|
93
|
-
* @param {number} page
|
|
94
|
-
* @param {number} perPage
|
|
95
|
-
* @returns {Promise<Pagination>}
|
|
96
|
-
*/
|
|
97
|
-
async paginate(page = 1, perPage = 20) {
|
|
98
|
-
// Model 서브클래스에서 오버라이드하여 실제 DB 쿼리 실행
|
|
99
|
-
return {
|
|
100
|
-
data: [],
|
|
101
|
-
page,
|
|
102
|
-
perPage,
|
|
103
|
-
total: 0,
|
|
104
|
-
lastPage: 0,
|
|
105
|
-
_query: this._buildQuery(),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* 전체 결과 반환
|
|
111
|
-
* @returns {Promise<Array>}
|
|
112
|
-
*/
|
|
113
|
-
async get() {
|
|
114
|
-
// 서브클래스에서 오버라이드
|
|
115
|
-
return [];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* 단일 결과
|
|
120
|
-
* @returns {Promise<object|null>}
|
|
121
|
-
*/
|
|
122
|
-
async first() {
|
|
123
|
-
this._limit = 1;
|
|
124
|
-
const results = await this.get();
|
|
125
|
-
return results[0] || null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* 결과 수
|
|
130
|
-
* @returns {Promise<number>}
|
|
131
|
-
*/
|
|
132
|
-
async count() {
|
|
133
|
-
// 서브클래스에서 오버라이드
|
|
134
|
-
return 0;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* 조건부 수정
|
|
139
|
-
* @param {object} data
|
|
140
|
-
* @returns {Promise<number>} 수정된 행 수
|
|
141
|
-
*/
|
|
142
|
-
async update(data) {
|
|
143
|
-
// 서브클래스에서 오버라이드
|
|
144
|
-
return 0;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* 조건부 삭제
|
|
149
|
-
* @returns {Promise<number>} 삭제된 행 수
|
|
150
|
-
*/
|
|
151
|
-
async delete() {
|
|
152
|
-
// 서브클래스에서 오버라이드
|
|
153
|
-
return 0;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* 빌드된 쿼리 파라미터 (디버깅/테스트용)
|
|
158
|
-
*/
|
|
159
|
-
_buildQuery() {
|
|
160
|
-
return {
|
|
161
|
-
wheres: this._wheres,
|
|
162
|
-
orders: this._orders,
|
|
163
|
-
limit: this._limit,
|
|
164
|
-
offset: this._offset,
|
|
165
|
-
selects: this._selects,
|
|
166
|
-
withs: this._withs,
|
|
167
|
-
softDelete: this._softDelete,
|
|
168
|
-
withTrashed: this._withTrashed,
|
|
169
|
-
onlyTrashed: this._onlyTrashed,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* QueryBuilder — SQL/MongoDB 쿼리 빌더 (체이닝)
|
|
3
|
+
*
|
|
4
|
+
* @see docs/framework/02-database-orm.md
|
|
5
|
+
* @see docs/framework/class-design.mm.md (QueryBuilder)
|
|
6
|
+
*/
|
|
7
|
+
export default class QueryBuilder {
|
|
8
|
+
constructor(model) {
|
|
9
|
+
this._model = model;
|
|
10
|
+
this._wheres = [];
|
|
11
|
+
this._orders = [];
|
|
12
|
+
this._limit = null;
|
|
13
|
+
this._offset = null;
|
|
14
|
+
this._selects = null;
|
|
15
|
+
this._withs = [];
|
|
16
|
+
// softDelete 자동 적용
|
|
17
|
+
this._softDelete = model?.softDelete || false;
|
|
18
|
+
this._withTrashed = false;
|
|
19
|
+
this._onlyTrashed = false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
23
|
+
// 체이닝 메서드
|
|
24
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
25
|
+
|
|
26
|
+
where(key, op, value) {
|
|
27
|
+
if (value === undefined) { value = op; op = '='; }
|
|
28
|
+
this._wheres.push({ key, op, value, type: 'and' });
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** OR 조건 */
|
|
33
|
+
orWhere(key, op, value) {
|
|
34
|
+
if (value === undefined) { value = op; op = '='; }
|
|
35
|
+
this._wheres.push({ key, op, value, type: 'or' });
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** IN 조건 */
|
|
40
|
+
whereIn(key, values) {
|
|
41
|
+
this._wheres.push({ key, op: 'IN', value: values, type: 'and' });
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** NOT NULL 조건 */
|
|
46
|
+
whereNotNull(key) {
|
|
47
|
+
this._wheres.push({ key, op: 'IS NOT NULL', value: null, type: 'and' });
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** NULL 조건 */
|
|
52
|
+
whereNull(key) {
|
|
53
|
+
this._wheres.push({ key, op: 'IS NULL', value: null, type: 'and' });
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
orderBy(column, direction = 'asc') {
|
|
58
|
+
this._orders.push({ column, direction });
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
limit(n) { this._limit = n; return this; }
|
|
63
|
+
offset(n) { this._offset = n; return this; }
|
|
64
|
+
|
|
65
|
+
select(...columns) {
|
|
66
|
+
this._selects = columns.flat();
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
with(...relations) {
|
|
71
|
+
this._withs.push(...relations.flat());
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** softDelete 포함 (삭제된 레코드도 조회) */
|
|
76
|
+
withTrashed() {
|
|
77
|
+
this._withTrashed = true;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** softDelete만 조회 (삭제된 레코드만) */
|
|
82
|
+
onlyTrashed() {
|
|
83
|
+
this._onlyTrashed = true;
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
88
|
+
// 터미널 메서드
|
|
89
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 페이지네이션
|
|
93
|
+
* @param {number} page
|
|
94
|
+
* @param {number} perPage
|
|
95
|
+
* @returns {Promise<Pagination>}
|
|
96
|
+
*/
|
|
97
|
+
async paginate(page = 1, perPage = 20) {
|
|
98
|
+
// Model 서브클래스에서 오버라이드하여 실제 DB 쿼리 실행
|
|
99
|
+
return {
|
|
100
|
+
data: [],
|
|
101
|
+
page,
|
|
102
|
+
perPage,
|
|
103
|
+
total: 0,
|
|
104
|
+
lastPage: 0,
|
|
105
|
+
_query: this._buildQuery(),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 전체 결과 반환
|
|
111
|
+
* @returns {Promise<Array>}
|
|
112
|
+
*/
|
|
113
|
+
async get() {
|
|
114
|
+
// 서브클래스에서 오버라이드
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 단일 결과
|
|
120
|
+
* @returns {Promise<object|null>}
|
|
121
|
+
*/
|
|
122
|
+
async first() {
|
|
123
|
+
this._limit = 1;
|
|
124
|
+
const results = await this.get();
|
|
125
|
+
return results[0] || null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 결과 수
|
|
130
|
+
* @returns {Promise<number>}
|
|
131
|
+
*/
|
|
132
|
+
async count() {
|
|
133
|
+
// 서브클래스에서 오버라이드
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 조건부 수정
|
|
139
|
+
* @param {object} data
|
|
140
|
+
* @returns {Promise<number>} 수정된 행 수
|
|
141
|
+
*/
|
|
142
|
+
async update(data) {
|
|
143
|
+
// 서브클래스에서 오버라이드
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 조건부 삭제
|
|
149
|
+
* @returns {Promise<number>} 삭제된 행 수
|
|
150
|
+
*/
|
|
151
|
+
async delete() {
|
|
152
|
+
// 서브클래스에서 오버라이드
|
|
153
|
+
return 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 빌드된 쿼리 파라미터 (디버깅/테스트용)
|
|
158
|
+
*/
|
|
159
|
+
_buildQuery() {
|
|
160
|
+
return {
|
|
161
|
+
wheres: this._wheres,
|
|
162
|
+
orders: this._orders,
|
|
163
|
+
limit: this._limit,
|
|
164
|
+
offset: this._offset,
|
|
165
|
+
selects: this._selects,
|
|
166
|
+
withs: this._withs,
|
|
167
|
+
softDelete: this._softDelete,
|
|
168
|
+
withTrashed: this._withTrashed,
|
|
169
|
+
onlyTrashed: this._onlyTrashed,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQLiteModel — SQLite 모델 서브클래스 (기본 드라이버)
|
|
3
|
-
*
|
|
4
|
-
* better-sqlite3 기반 동기 API.
|
|
5
|
-
* 개발 환경에서 별도 DB 서버 없이 즉시 사용 가능.
|
|
6
|
-
*
|
|
7
|
-
* @see docs/framework/02-database-orm.md
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* import { SQLiteModel } from '@fuzionx/framework';
|
|
11
|
-
*
|
|
12
|
-
* export default class User extends SQLiteModel {
|
|
13
|
-
* static table = 'users';
|
|
14
|
-
* static timestamps = true;
|
|
15
|
-
* static columns = {
|
|
16
|
-
* id: { type: 'increments' },
|
|
17
|
-
* name: { type: 'string', length: 100 },
|
|
18
|
-
* email: { type: 'string', length: 150, unique: true },
|
|
19
|
-
* };
|
|
20
|
-
* }
|
|
21
|
-
*/
|
|
22
|
-
import SqlModel from './SqlModel.js';
|
|
23
|
-
|
|
24
|
-
export default class SQLiteModel extends SqlModel {
|
|
25
|
-
static driver = 'sqlite';
|
|
26
|
-
static connection = 'main';
|
|
27
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* SQLiteModel — SQLite 모델 서브클래스 (기본 드라이버)
|
|
3
|
+
*
|
|
4
|
+
* better-sqlite3 기반 동기 API.
|
|
5
|
+
* 개발 환경에서 별도 DB 서버 없이 즉시 사용 가능.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/framework/02-database-orm.md
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { SQLiteModel } from '@fuzionx/framework';
|
|
11
|
+
*
|
|
12
|
+
* export default class User extends SQLiteModel {
|
|
13
|
+
* static table = 'users';
|
|
14
|
+
* static timestamps = true;
|
|
15
|
+
* static columns = {
|
|
16
|
+
* id: { type: 'increments' },
|
|
17
|
+
* name: { type: 'string', length: 100 },
|
|
18
|
+
* email: { type: 'string', length: 150, unique: true },
|
|
19
|
+
* };
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
import SqlModel from './SqlModel.js';
|
|
23
|
+
|
|
24
|
+
export default class SQLiteModel extends SqlModel {
|
|
25
|
+
static driver = 'sqlite';
|
|
26
|
+
static connection = 'main';
|
|
27
|
+
}
|