@fuzionx/framework 0.1.43 → 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.
- package/README.md +501 -501
- package/bin/fx.js +12 -12
- package/cli/db-sync.js +100 -100
- package/cli/index.js +494 -494
- 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 -1006
- package/lib/core/AutoLoader.js +227 -227
- package/lib/core/Base.js +64 -64
- package/lib/core/Config.js +331 -331
- package/lib/core/Context.js +484 -484
- 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 -332
- 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 -108
- 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 -125
- 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 -103
- package/lib/schedule/Scheduler.js +171 -171
- 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
|
@@ -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
|
+
}
|