@fuzionx/framework 0.1.39 → 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 -1006
- 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 -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
package/lib/helpers/Logger.js
CHANGED
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logger — Bridge tracing 통합 로거
|
|
3
|
-
*
|
|
4
|
-
* Bridge(Rust) logInfo/logWarn/logError/logDebug N-API에 위임.
|
|
5
|
-
* Bridge 없으면 JS console 폴백.
|
|
6
|
-
*
|
|
7
|
-
* @see docs/framework/13-logging.md
|
|
8
|
-
* @see packages/fuzionx/lib/logger.js (Core 래퍼 참조)
|
|
9
|
-
*/
|
|
10
|
-
import { format } from 'node:util';
|
|
11
|
-
|
|
12
|
-
export default class Logger {
|
|
13
|
-
/**
|
|
14
|
-
* @param {object} [opts]
|
|
15
|
-
* @param {string} [opts.level='info'] - 최소 출력 레벨
|
|
16
|
-
* @param {object} [opts.bridge] - Bridge N-API 인스턴스
|
|
17
|
-
* @param {boolean} [opts.json=false] - JSON 포맷 (Bridge 없을 때)
|
|
18
|
-
* @param {string} [opts.prefix='app'] - Bridge target 이름
|
|
19
|
-
*/
|
|
20
|
-
constructor(opts = {}) {
|
|
21
|
-
this.level = opts.level || 'info';
|
|
22
|
-
this._bridge = opts.bridge || null;
|
|
23
|
-
this._json = opts.json || false;
|
|
24
|
-
this._prefix = opts.prefix || 'app';
|
|
25
|
-
this._levels = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
26
|
-
this._minLevel = this._levels[this.level] ?? 2;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @param {string} message
|
|
31
|
-
* @param {object} [context]
|
|
32
|
-
*/
|
|
33
|
-
error(message, context) { this._log('error', message, context); }
|
|
34
|
-
warn(message, context) { this._log('warn', message, context); }
|
|
35
|
-
info(message, context) { this._log('info', message, context); }
|
|
36
|
-
debug(message, context) { this._log('debug', message, context); }
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 자식 로거 생성 (prefix 상속)
|
|
40
|
-
* @param {string} prefix
|
|
41
|
-
* @returns {Logger}
|
|
42
|
-
*/
|
|
43
|
-
child(prefix) {
|
|
44
|
-
return new Logger({
|
|
45
|
-
level: this.level,
|
|
46
|
-
bridge: this._bridge,
|
|
47
|
-
json: this._json,
|
|
48
|
-
prefix: this._prefix ? `${this._prefix}:${prefix}` : prefix,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** @private */
|
|
53
|
-
_log(level, message, context) {
|
|
54
|
-
if (this._levels[level] > this._minLevel) return;
|
|
55
|
-
|
|
56
|
-
// Error 객체면 전체 스택 포함
|
|
57
|
-
const isErr = message instanceof Error;
|
|
58
|
-
const text = isErr ? (message.stack || `${message}`) : `${message}`;
|
|
59
|
-
|
|
60
|
-
// ── Bridge N-API 위임 (context 결합은 Bridge 경로에서만) ──
|
|
61
|
-
if (this._bridge) {
|
|
62
|
-
// Bridge는 단일 문자열만 받으므로 여기서만 concat
|
|
63
|
-
const msg = context ? `${text} ${JSON.stringify(context)}` : text;
|
|
64
|
-
try {
|
|
65
|
-
if (level === 'info' && typeof this._bridge.logInfo === 'function') {
|
|
66
|
-
this._bridge.logInfo(this._prefix, msg);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (level === 'warn' && typeof this._bridge.logWarn === 'function') {
|
|
70
|
-
this._bridge.logWarn(this._prefix, msg);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
if (level === 'error' && typeof this._bridge.logError === 'function') {
|
|
74
|
-
this._bridge.logError(this._prefix, msg);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if (level === 'debug' && typeof this._bridge.logDebug === 'function') {
|
|
78
|
-
this._bridge.logDebug(this._prefix, msg);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
} catch {} // Bridge 호출 실패 시 JS 폴백
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ── JS 폴백 ──
|
|
85
|
-
const timestamp = new Date().toISOString();
|
|
86
|
-
|
|
87
|
-
if (this._json) {
|
|
88
|
-
const entry = {
|
|
89
|
-
timestamp,
|
|
90
|
-
level,
|
|
91
|
-
target: this._prefix,
|
|
92
|
-
message: text,
|
|
93
|
-
...(context || {}),
|
|
94
|
-
};
|
|
95
|
-
console[level === 'debug' ? 'log' : level](JSON.stringify(entry));
|
|
96
|
-
} else {
|
|
97
|
-
const levelTag = level.toUpperCase().padEnd(5);
|
|
98
|
-
const fullMsg = context
|
|
99
|
-
? `${timestamp} ${levelTag} [${this._prefix}] ${text}`
|
|
100
|
-
: `${timestamp} ${levelTag} [${this._prefix}] ${text}`;
|
|
101
|
-
if (context && Object.keys(context).length > 0) {
|
|
102
|
-
console[level === 'debug' ? 'log' : level](fullMsg, context);
|
|
103
|
-
} else {
|
|
104
|
-
console[level === 'debug' ? 'log' : level](fullMsg);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Logger — Bridge tracing 통합 로거
|
|
3
|
+
*
|
|
4
|
+
* Bridge(Rust) logInfo/logWarn/logError/logDebug N-API에 위임.
|
|
5
|
+
* Bridge 없으면 JS console 폴백.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/framework/13-logging.md
|
|
8
|
+
* @see packages/fuzionx/lib/logger.js (Core 래퍼 참조)
|
|
9
|
+
*/
|
|
10
|
+
import { format } from 'node:util';
|
|
11
|
+
|
|
12
|
+
export default class Logger {
|
|
13
|
+
/**
|
|
14
|
+
* @param {object} [opts]
|
|
15
|
+
* @param {string} [opts.level='info'] - 최소 출력 레벨
|
|
16
|
+
* @param {object} [opts.bridge] - Bridge N-API 인스턴스
|
|
17
|
+
* @param {boolean} [opts.json=false] - JSON 포맷 (Bridge 없을 때)
|
|
18
|
+
* @param {string} [opts.prefix='app'] - Bridge target 이름
|
|
19
|
+
*/
|
|
20
|
+
constructor(opts = {}) {
|
|
21
|
+
this.level = opts.level || 'info';
|
|
22
|
+
this._bridge = opts.bridge || null;
|
|
23
|
+
this._json = opts.json || false;
|
|
24
|
+
this._prefix = opts.prefix || 'app';
|
|
25
|
+
this._levels = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
26
|
+
this._minLevel = this._levels[this.level] ?? 2;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} message
|
|
31
|
+
* @param {object} [context]
|
|
32
|
+
*/
|
|
33
|
+
error(message, context) { this._log('error', message, context); }
|
|
34
|
+
warn(message, context) { this._log('warn', message, context); }
|
|
35
|
+
info(message, context) { this._log('info', message, context); }
|
|
36
|
+
debug(message, context) { this._log('debug', message, context); }
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 자식 로거 생성 (prefix 상속)
|
|
40
|
+
* @param {string} prefix
|
|
41
|
+
* @returns {Logger}
|
|
42
|
+
*/
|
|
43
|
+
child(prefix) {
|
|
44
|
+
return new Logger({
|
|
45
|
+
level: this.level,
|
|
46
|
+
bridge: this._bridge,
|
|
47
|
+
json: this._json,
|
|
48
|
+
prefix: this._prefix ? `${this._prefix}:${prefix}` : prefix,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** @private */
|
|
53
|
+
_log(level, message, context) {
|
|
54
|
+
if (this._levels[level] > this._minLevel) return;
|
|
55
|
+
|
|
56
|
+
// Error 객체면 전체 스택 포함
|
|
57
|
+
const isErr = message instanceof Error;
|
|
58
|
+
const text = isErr ? (message.stack || `${message}`) : `${message}`;
|
|
59
|
+
|
|
60
|
+
// ── Bridge N-API 위임 (context 결합은 Bridge 경로에서만) ──
|
|
61
|
+
if (this._bridge) {
|
|
62
|
+
// Bridge는 단일 문자열만 받으므로 여기서만 concat
|
|
63
|
+
const msg = context ? `${text} ${JSON.stringify(context)}` : text;
|
|
64
|
+
try {
|
|
65
|
+
if (level === 'info' && typeof this._bridge.logInfo === 'function') {
|
|
66
|
+
this._bridge.logInfo(this._prefix, msg);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (level === 'warn' && typeof this._bridge.logWarn === 'function') {
|
|
70
|
+
this._bridge.logWarn(this._prefix, msg);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (level === 'error' && typeof this._bridge.logError === 'function') {
|
|
74
|
+
this._bridge.logError(this._prefix, msg);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (level === 'debug' && typeof this._bridge.logDebug === 'function') {
|
|
78
|
+
this._bridge.logDebug(this._prefix, msg);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
} catch {} // Bridge 호출 실패 시 JS 폴백
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── JS 폴백 ──
|
|
85
|
+
const timestamp = new Date().toISOString();
|
|
86
|
+
|
|
87
|
+
if (this._json) {
|
|
88
|
+
const entry = {
|
|
89
|
+
timestamp,
|
|
90
|
+
level,
|
|
91
|
+
target: this._prefix,
|
|
92
|
+
message: text,
|
|
93
|
+
...(context || {}),
|
|
94
|
+
};
|
|
95
|
+
console[level === 'debug' ? 'log' : level](JSON.stringify(entry));
|
|
96
|
+
} else {
|
|
97
|
+
const levelTag = level.toUpperCase().padEnd(5);
|
|
98
|
+
const fullMsg = context
|
|
99
|
+
? `${timestamp} ${levelTag} [${this._prefix}] ${text}`
|
|
100
|
+
: `${timestamp} ${levelTag} [${this._prefix}] ${text}`;
|
|
101
|
+
if (context && Object.keys(context).length > 0) {
|
|
102
|
+
console[level === 'debug' ? 'log' : level](fullMsg, context);
|
|
103
|
+
} else {
|
|
104
|
+
console[level === 'debug' ? 'log' : level](fullMsg);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MediaHelper — Bridge media N-API 래퍼
|
|
3
|
-
*
|
|
4
|
-
* @see docs/framework/19-utilities.md — "3. Media (app.media)"
|
|
5
|
-
* @see packages/fuzionx/lib/media.js (Core 래퍼)
|
|
6
|
-
*/
|
|
7
|
-
export default class MediaHelper {
|
|
8
|
-
constructor(bridge) { this._bridge = bridge; }
|
|
9
|
-
|
|
10
|
-
resize(input, output, width, height, format = 'webp', quality = 80) {
|
|
11
|
-
if (this._bridge?.mediaResize) return this._bridge.mediaResize(input, output, width, height, format, quality);
|
|
12
|
-
throw new Error('Media operations require Bridge (Rust). Install @fuzionx/bridge.');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
resizeMultiple(input, outputDir, baseName, specs) {
|
|
16
|
-
if (this._bridge?.mediaResizeMultiple) return this._bridge.mediaResizeMultiple(input, outputDir, baseName, JSON.stringify(specs));
|
|
17
|
-
throw new Error('Media operations require Bridge (Rust).');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
imageInfo(filePath) {
|
|
21
|
-
if (this._bridge?.mediaGetImageInfo) return this._bridge.mediaGetImageInfo(filePath);
|
|
22
|
-
throw new Error('Image info requires Bridge (Rust).');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
toWebp(input, output, quality = 80) {
|
|
26
|
-
return this.resize(input, output, 0, 0, 'webp', quality);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
videoThumbnail(input, output, atSeconds = 1, width = 640, format = 'jpeg') {
|
|
30
|
-
if (this._bridge?.mediaVideoThumbnail) return this._bridge.mediaVideoThumbnail(input, output, atSeconds, width, format);
|
|
31
|
-
throw new Error('Video operations require ffmpeg + Bridge.');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
videoInfo(filePath) {
|
|
35
|
-
if (this._bridge?.mediaVideoInfo) return this._bridge.mediaVideoInfo(filePath);
|
|
36
|
-
throw new Error('Video info requires ffprobe + Bridge.');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* 이미지에 워터마크를 합성한다.
|
|
41
|
-
* @param {string} targetPath - 대상 이미지 경로
|
|
42
|
-
* @param {string} watermarkPath - 워터마크 이미지 경로
|
|
43
|
-
* @param {number} [opacity=50] - 불투명도 (0-100)
|
|
44
|
-
* @param {string} [format] - 출력 포맷 (기본: 원본 확장자)
|
|
45
|
-
* @param {number} [quality=90] - 출력 품질
|
|
46
|
-
*/
|
|
47
|
-
applyWatermark(targetPath, watermarkPath, opacity = 50, format, quality = 90) {
|
|
48
|
-
if (this._bridge?.mediaApplyWatermark) {
|
|
49
|
-
return this._bridge.mediaApplyWatermark(targetPath, watermarkPath, opacity, format, quality);
|
|
50
|
-
}
|
|
51
|
-
throw new Error('Watermark requires Bridge (Rust).');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 비디오에서 N초 간격으로 다중 썸네일을 추출한다.
|
|
56
|
-
* @param {string} inputPath - 비디오 경로
|
|
57
|
-
* @param {string} outputDir - 출력 디렉토리
|
|
58
|
-
* @param {number} [interval=5] - 초 간격
|
|
59
|
-
* @param {number} [width=320] - 썸네일 가로 크기
|
|
60
|
-
* @param {string} [format='jpeg'] - 포맷
|
|
61
|
-
* @returns {string[]} 생성된 파일 경로 목록
|
|
62
|
-
*/
|
|
63
|
-
videoThumbnails(inputPath, outputDir, interval = 5, width = 320, format = 'jpeg') {
|
|
64
|
-
if (this._bridge?.mediaVideoThumbnails) {
|
|
65
|
-
return this._bridge.mediaVideoThumbnails(inputPath, outputDir, interval, width, format);
|
|
66
|
-
}
|
|
67
|
-
throw new Error('Video thumbnails requires ffmpeg + Bridge.');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 썸네일 목록을 스프라이트 시트(그리드)로 합성한다.
|
|
72
|
-
* @param {string[]} thumbPaths - 썸네일 경로 목록
|
|
73
|
-
* @param {string} outputPath - 출력 스프라이트 시트 경로
|
|
74
|
-
* @param {number} [cols=10] - 그리드 열 수
|
|
75
|
-
* @param {number} [thumbWidth=160] - 각 썸네일 가로
|
|
76
|
-
* @param {number} [thumbHeight=0] - 각 썸네일 세로 (0=자동)
|
|
77
|
-
*/
|
|
78
|
-
videoPreviewSheet(thumbPaths, outputPath, cols = 10, thumbWidth = 160, thumbHeight = 0) {
|
|
79
|
-
if (this._bridge?.mediaVideoPreviewSheet) {
|
|
80
|
-
return this._bridge.mediaVideoPreviewSheet(thumbPaths, outputPath, cols, thumbWidth, thumbHeight);
|
|
81
|
-
}
|
|
82
|
-
throw new Error('Preview sheet requires Bridge (Rust).');
|
|
83
|
-
}
|
|
84
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* MediaHelper — Bridge media N-API 래퍼
|
|
3
|
+
*
|
|
4
|
+
* @see docs/framework/19-utilities.md — "3. Media (app.media)"
|
|
5
|
+
* @see packages/fuzionx/lib/media.js (Core 래퍼)
|
|
6
|
+
*/
|
|
7
|
+
export default class MediaHelper {
|
|
8
|
+
constructor(bridge) { this._bridge = bridge; }
|
|
9
|
+
|
|
10
|
+
resize(input, output, width, height, format = 'webp', quality = 80) {
|
|
11
|
+
if (this._bridge?.mediaResize) return this._bridge.mediaResize(input, output, width, height, format, quality);
|
|
12
|
+
throw new Error('Media operations require Bridge (Rust). Install @fuzionx/bridge.');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resizeMultiple(input, outputDir, baseName, specs) {
|
|
16
|
+
if (this._bridge?.mediaResizeMultiple) return this._bridge.mediaResizeMultiple(input, outputDir, baseName, JSON.stringify(specs));
|
|
17
|
+
throw new Error('Media operations require Bridge (Rust).');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
imageInfo(filePath) {
|
|
21
|
+
if (this._bridge?.mediaGetImageInfo) return this._bridge.mediaGetImageInfo(filePath);
|
|
22
|
+
throw new Error('Image info requires Bridge (Rust).');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
toWebp(input, output, quality = 80) {
|
|
26
|
+
return this.resize(input, output, 0, 0, 'webp', quality);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
videoThumbnail(input, output, atSeconds = 1, width = 640, format = 'jpeg') {
|
|
30
|
+
if (this._bridge?.mediaVideoThumbnail) return this._bridge.mediaVideoThumbnail(input, output, atSeconds, width, format);
|
|
31
|
+
throw new Error('Video operations require ffmpeg + Bridge.');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
videoInfo(filePath) {
|
|
35
|
+
if (this._bridge?.mediaVideoInfo) return this._bridge.mediaVideoInfo(filePath);
|
|
36
|
+
throw new Error('Video info requires ffprobe + Bridge.');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 이미지에 워터마크를 합성한다.
|
|
41
|
+
* @param {string} targetPath - 대상 이미지 경로
|
|
42
|
+
* @param {string} watermarkPath - 워터마크 이미지 경로
|
|
43
|
+
* @param {number} [opacity=50] - 불투명도 (0-100)
|
|
44
|
+
* @param {string} [format] - 출력 포맷 (기본: 원본 확장자)
|
|
45
|
+
* @param {number} [quality=90] - 출력 품질
|
|
46
|
+
*/
|
|
47
|
+
applyWatermark(targetPath, watermarkPath, opacity = 50, format, quality = 90) {
|
|
48
|
+
if (this._bridge?.mediaApplyWatermark) {
|
|
49
|
+
return this._bridge.mediaApplyWatermark(targetPath, watermarkPath, opacity, format, quality);
|
|
50
|
+
}
|
|
51
|
+
throw new Error('Watermark requires Bridge (Rust).');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 비디오에서 N초 간격으로 다중 썸네일을 추출한다.
|
|
56
|
+
* @param {string} inputPath - 비디오 경로
|
|
57
|
+
* @param {string} outputDir - 출력 디렉토리
|
|
58
|
+
* @param {number} [interval=5] - 초 간격
|
|
59
|
+
* @param {number} [width=320] - 썸네일 가로 크기
|
|
60
|
+
* @param {string} [format='jpeg'] - 포맷
|
|
61
|
+
* @returns {string[]} 생성된 파일 경로 목록
|
|
62
|
+
*/
|
|
63
|
+
videoThumbnails(inputPath, outputDir, interval = 5, width = 320, format = 'jpeg') {
|
|
64
|
+
if (this._bridge?.mediaVideoThumbnails) {
|
|
65
|
+
return this._bridge.mediaVideoThumbnails(inputPath, outputDir, interval, width, format);
|
|
66
|
+
}
|
|
67
|
+
throw new Error('Video thumbnails requires ffmpeg + Bridge.');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 썸네일 목록을 스프라이트 시트(그리드)로 합성한다.
|
|
72
|
+
* @param {string[]} thumbPaths - 썸네일 경로 목록
|
|
73
|
+
* @param {string} outputPath - 출력 스프라이트 시트 경로
|
|
74
|
+
* @param {number} [cols=10] - 그리드 열 수
|
|
75
|
+
* @param {number} [thumbWidth=160] - 각 썸네일 가로
|
|
76
|
+
* @param {number} [thumbHeight=0] - 각 썸네일 세로 (0=자동)
|
|
77
|
+
*/
|
|
78
|
+
videoPreviewSheet(thumbPaths, outputPath, cols = 10, thumbWidth = 160, thumbHeight = 0) {
|
|
79
|
+
if (this._bridge?.mediaVideoPreviewSheet) {
|
|
80
|
+
return this._bridge.mediaVideoPreviewSheet(thumbPaths, outputPath, cols, thumbWidth, thumbHeight);
|
|
81
|
+
}
|
|
82
|
+
throw new Error('Preview sheet requires Bridge (Rust).');
|
|
83
|
+
}
|
|
84
|
+
}
|
package/lib/http/Controller.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Controller — HTTP 요청 핸들러 기본 클래스
|
|
3
|
-
*
|
|
4
|
-
* 싱글톤: 부트 시 1회 생성. 핸들러 메서드는 ctx만으로 동작.
|
|
5
|
-
*
|
|
6
|
-
* @see docs/framework/01-routing-controllers.md
|
|
7
|
-
* @see docs/framework/class-design.mm.md (Controller)
|
|
8
|
-
*/
|
|
9
|
-
import Base from '../core/Base.js';
|
|
10
|
-
|
|
11
|
-
export default class Controller extends Base {
|
|
12
|
-
/** @type {string[]} 이 컨트롤러 전체에 적용할 미들웨어 */
|
|
13
|
-
static middleware = [];
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 프레임워크 내부 — 컨트롤러 스캔 시 프로토타입 메서드를 static 레퍼런스로 자동 등록
|
|
17
|
-
* @param {typeof Controller} ControllerClass
|
|
18
|
-
*/
|
|
19
|
-
static register(ControllerClass) {
|
|
20
|
-
const proto = ControllerClass.prototype;
|
|
21
|
-
const baseNames = new Set(Object.getOwnPropertyNames(Base.prototype));
|
|
22
|
-
for (const method of Object.getOwnPropertyNames(proto)) {
|
|
23
|
-
if (method === 'constructor') continue;
|
|
24
|
-
// Base 메서드/getter 전체 스킵 (service, emit, dispatch, ws, crypto, i18n)
|
|
25
|
-
if (baseNames.has(method)) continue;
|
|
26
|
-
|
|
27
|
-
ControllerClass[method] = {
|
|
28
|
-
__handler__: true,
|
|
29
|
-
controller: ControllerClass,
|
|
30
|
-
method,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Controller — HTTP 요청 핸들러 기본 클래스
|
|
3
|
+
*
|
|
4
|
+
* 싱글톤: 부트 시 1회 생성. 핸들러 메서드는 ctx만으로 동작.
|
|
5
|
+
*
|
|
6
|
+
* @see docs/framework/01-routing-controllers.md
|
|
7
|
+
* @see docs/framework/class-design.mm.md (Controller)
|
|
8
|
+
*/
|
|
9
|
+
import Base from '../core/Base.js';
|
|
10
|
+
|
|
11
|
+
export default class Controller extends Base {
|
|
12
|
+
/** @type {string[]} 이 컨트롤러 전체에 적용할 미들웨어 */
|
|
13
|
+
static middleware = [];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 프레임워크 내부 — 컨트롤러 스캔 시 프로토타입 메서드를 static 레퍼런스로 자동 등록
|
|
17
|
+
* @param {typeof Controller} ControllerClass
|
|
18
|
+
*/
|
|
19
|
+
static register(ControllerClass) {
|
|
20
|
+
const proto = ControllerClass.prototype;
|
|
21
|
+
const baseNames = new Set(Object.getOwnPropertyNames(Base.prototype));
|
|
22
|
+
for (const method of Object.getOwnPropertyNames(proto)) {
|
|
23
|
+
if (method === 'constructor') continue;
|
|
24
|
+
// Base 메서드/getter 전체 스킵 (service, emit, dispatch, ws, crypto, i18n)
|
|
25
|
+
if (baseNames.has(method)) continue;
|
|
26
|
+
|
|
27
|
+
ControllerClass[method] = {
|
|
28
|
+
__handler__: true,
|
|
29
|
+
controller: ControllerClass,
|
|
30
|
+
method,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|