@hamjimin/xplat-back 0.5.0 → 0.6.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 (30) hide show
  1. package/dist/cli/index.js +5 -0
  2. package/dist/cli/init.d.ts +1 -0
  3. package/dist/cli/init.js +93 -2
  4. package/dist/cli/templates/app.ts.example +3 -0
  5. package/dist/cli/templates/next/app/globals.css.example +15 -0
  6. package/dist/cli/templates/next/app/layout.tsx.example +16 -0
  7. package/dist/cli/templates/next/app/page.tsx.example +12 -0
  8. package/dist/cli/templates/next/next-env.d.ts.example +5 -0
  9. package/dist/cli/templates/next/next.config.js.example +9 -0
  10. package/dist/cli/templates/next/package.json.example +24 -0
  11. package/dist/cli/templates/next/tsconfig.json.example +23 -0
  12. package/dist/cli/templates/package.json.example +4 -1
  13. package/dist/cli/templates/util/dbconfig/dbConnections.ts.example +92 -0
  14. package/dist/commerce/commerce.module.d.ts +7 -0
  15. package/dist/commerce/commerce.module.js +25 -0
  16. package/dist/commerce/modules/admin/admin-api.module.d.ts +8 -0
  17. package/dist/commerce/modules/admin/admin-api.module.js +25 -0
  18. package/dist/commerce/modules/admin/controllers/admin-health.controller.d.ts +11 -0
  19. package/dist/commerce/modules/admin/controllers/admin-health.controller.js +36 -0
  20. package/dist/commerce/modules/store/controllers/store-health.controller.d.ts +11 -0
  21. package/dist/commerce/modules/store/controllers/store-health.controller.js +36 -0
  22. package/dist/commerce/modules/store/store-api.module.d.ts +8 -0
  23. package/dist/commerce/modules/store/store-api.module.js +25 -0
  24. package/dist/commerce/types.d.ts +33 -0
  25. package/dist/commerce/types.js +5 -0
  26. package/dist/core/XplatSystem.d.ts +2 -0
  27. package/dist/core/XplatSystem.js +2 -0
  28. package/dist/modules/commerce/index.d.ts +15 -0
  29. package/dist/modules/commerce/index.js +21 -0
  30. package/package.json +9 -3
package/dist/cli/index.js CHANGED
@@ -28,6 +28,10 @@ async function main() {
28
28
  case '--router-path':
29
29
  config.routerBasePath = args[++i];
30
30
  break;
31
+ case '--with-next':
32
+ case '--next':
33
+ config.withNext = true;
34
+ break;
31
35
  case '--help':
32
36
  case '-h':
33
37
  showHelp();
@@ -64,6 +68,7 @@ Options:
64
68
  -c, --cors <origins> CORS 허용 origins (쉼표로 구분, 기본값: http://localhost:3000)
65
69
  -r, --router-dir <directory> 라우터 디렉토리 (기본값: src/restapi)
66
70
  --router-path <path> 라우터 base path (기본값: /restapi)
71
+ --with-next Next.js 프론트엔드(web) 템플릿 추가
67
72
  -h, --help 도움말 출력
68
73
 
69
74
  Examples:
@@ -7,6 +7,7 @@ export interface InitConfig {
7
7
  corsOrigins?: string;
8
8
  routerDirectory?: string;
9
9
  routerBasePath?: string;
10
+ withNext?: boolean;
10
11
  }
11
12
  /**
12
13
  * 프로젝트 초기화 로직
package/dist/cli/init.js CHANGED
@@ -48,6 +48,7 @@ async function initializeProject(config = {}) {
48
48
  const corsOrigins = config.corsOrigins || 'http://localhost:3000';
49
49
  const routerDirectory = config.routerDirectory || 'src/restapi';
50
50
  const routerBasePath = config.routerBasePath || '/restapi';
51
+ const withNext = config.withNext || false;
51
52
  console.log('\n🚀 xplat-back 프로젝트 초기화를 시작합니다...\n');
52
53
  // src 디렉토리 생성
53
54
  const srcDir = path.join(projectRoot, 'src');
@@ -75,6 +76,12 @@ async function initializeProject(config = {}) {
75
76
  await createExampleRouteFile(restapiDir);
76
77
  // 상수 파일 생성 (src/const 디렉토리)
77
78
  await createConstantsFiles(srcDir);
79
+ // DB 설정 파일 생성 (src/util/dbconfig)
80
+ await createDbConfigFiles(srcDir);
81
+ // Next.js 앱 생성 (옵션)
82
+ if (withNext) {
83
+ await createNextApp(projectRoot);
84
+ }
78
85
  console.log('\n✅ 프로젝트 초기화가 완료되었습니다!');
79
86
  console.log(`\n생성된 파일:`);
80
87
  console.log(` - src/app.ts`);
@@ -85,6 +92,12 @@ async function initializeProject(config = {}) {
85
92
  console.log(` - ${routerDirectory}/health.ts`);
86
93
  console.log(` - src/const/api_code.ts`);
87
94
  console.log(` - src/const/constants.ts`);
95
+ console.log(` - src/util/dbconfig/dbConnections.ts`);
96
+ if (withNext) {
97
+ console.log(` - web/next.config.js`);
98
+ console.log(` - web/app/page.tsx`);
99
+ console.log(` - web/package.json`);
100
+ }
88
101
  console.log(`\n다음 단계:`);
89
102
  console.log(` 1. npm install (또는 yarn install)`);
90
103
  console.log(` 2. npm run dev (또는 yarn dev)로 서버 실행`);
@@ -101,8 +114,11 @@ async function createAppFile(projectRoot, port, corsOrigins, routerDirectory, ro
101
114
  }
102
115
  const appContent = `import express, { Application } from 'express';
103
116
  import * as path from 'path';
117
+ import dotenv from 'dotenv';
104
118
  import { createApp } from 'xplat-back';
105
119
 
120
+ dotenv.config();
121
+
106
122
  const app: Application = createApp({
107
123
  cors: {
108
124
  origin: process.env.CORS_ALLOW_ORIGINS
@@ -218,7 +234,10 @@ async function createPackageJsonFile(projectRoot, projectName) {
218
234
  },
219
235
  dependencies: {
220
236
  express: '^4.18.2',
221
- 'xplat-back': '^0.1.0',
237
+ dotenv: '^16.4.5',
238
+ typeorm: '^0.3.20',
239
+ mysql2: '^3.9.7',
240
+ 'xplat-back': '^0.5.1',
222
241
  },
223
242
  devDependencies: {
224
243
  '@types/express': '^4.17.21',
@@ -247,7 +266,14 @@ PORT=${port}
247
266
  # CORS Configuration
248
267
  CORS_ALLOW_ORIGINS=${corsOrigins}
249
268
 
250
- # Add your environment variables below
269
+ # Database Configuration
270
+ DB_HOST=localhost
271
+ DB_PORT=3306
272
+ DB_USER=root
273
+ DB_PASSWORD=
274
+ DB_DATABASE=my_database
275
+ DB_MYSQL_PORT=3306
276
+ DB_SYNC=false
251
277
  `;
252
278
  fs.writeFileSync(envPath, envContent, 'utf-8');
253
279
  console.log(`✅ .env 파일이 생성되었습니다.`);
@@ -298,3 +324,68 @@ async function createConstantsFiles(srcDir) {
298
324
  console.log(`⚠️ src/const/constants.ts 파일이 이미 존재합니다. 건너뜁니다.`);
299
325
  }
300
326
  }
327
+ /**
328
+ * DB 설정 파일 생성 (src/util/dbconfig/dbConnections.ts)
329
+ */
330
+ async function createDbConfigFiles(srcDir) {
331
+ const dbConfigDir = path.join(srcDir, 'util', 'dbconfig');
332
+ if (!fs.existsSync(dbConfigDir)) {
333
+ fs.mkdirSync(dbConfigDir, { recursive: true });
334
+ }
335
+ const dbConnectionsPath = path.join(dbConfigDir, 'dbConnections.ts');
336
+ if (fs.existsSync(dbConnectionsPath)) {
337
+ console.log(`⚠️ src/util/dbconfig/dbConnections.ts 파일이 이미 존재합니다. 건너뜁니다.`);
338
+ return;
339
+ }
340
+ const templatePath = path.join(__dirname, 'templates', 'util', 'dbconfig', 'dbConnections.ts.example');
341
+ const templateContent = fs.readFileSync(templatePath, 'utf-8');
342
+ fs.writeFileSync(dbConnectionsPath, templateContent, 'utf-8');
343
+ console.log(`✅ src/util/dbconfig/dbConnections.ts 파일이 생성되었습니다.`);
344
+ }
345
+ /**
346
+ * Next.js 프로젝트 생성 (web 디렉토리)
347
+ */
348
+ async function createNextApp(projectRoot) {
349
+ const webDir = path.join(projectRoot, 'web');
350
+ if (!fs.existsSync(webDir)) {
351
+ fs.mkdirSync(webDir, { recursive: true });
352
+ }
353
+ const templateBase = path.join(__dirname, 'templates', 'next');
354
+ const filesToCopy = [
355
+ { template: 'package.json.example', target: 'package.json' },
356
+ { template: 'tsconfig.json.example', target: 'tsconfig.json' },
357
+ { template: 'next.config.js.example', target: 'next.config.js' },
358
+ ];
359
+ filesToCopy.forEach(({ template, target }) => {
360
+ const targetPath = path.join(webDir, target);
361
+ if (fs.existsSync(targetPath)) {
362
+ console.log(`⚠️ web/${target} 파일이 이미 존재합니다. 건너뜁니다.`);
363
+ return;
364
+ }
365
+ const templatePath = path.join(templateBase, template);
366
+ const content = fs.readFileSync(templatePath, 'utf-8');
367
+ fs.writeFileSync(targetPath, content, 'utf-8');
368
+ console.log(`✅ web/${target} 파일이 생성되었습니다.`);
369
+ });
370
+ const appDir = path.join(webDir, 'app');
371
+ if (!fs.existsSync(appDir)) {
372
+ fs.mkdirSync(appDir, { recursive: true });
373
+ console.log(`✅ web/app 디렉토리가 생성되었습니다.`);
374
+ }
375
+ const appFiles = [
376
+ { template: path.join('app', 'page.tsx.example'), target: path.join(appDir, 'page.tsx') },
377
+ { template: path.join('app', 'layout.tsx.example'), target: path.join(appDir, 'layout.tsx') },
378
+ { template: path.join('app', 'globals.css.example'), target: path.join(appDir, 'globals.css') },
379
+ { template: 'next-env.d.ts.example', target: path.join(webDir, 'next-env.d.ts') },
380
+ ];
381
+ appFiles.forEach(({ template, target }) => {
382
+ if (fs.existsSync(target)) {
383
+ console.log(`⚠️ ${path.relative(projectRoot, target)} 파일이 이미 존재합니다. 건너뜁니다.`);
384
+ return;
385
+ }
386
+ const templatePath = path.join(templateBase, template);
387
+ const content = fs.readFileSync(templatePath, 'utf-8');
388
+ fs.writeFileSync(target, content, 'utf-8');
389
+ console.log(`✅ ${path.relative(projectRoot, target)} 파일이 생성되었습니다.`);
390
+ });
391
+ }
@@ -1,7 +1,10 @@
1
1
  import express, { Application } from 'express';
2
2
  import * as path from 'path';
3
+ import dotenv from 'dotenv';
3
4
  import { createApp } from 'xplat-back';
4
5
 
6
+ dotenv.config();
7
+
5
8
  const app: Application = createApp({
6
9
  cors: {
7
10
  origin: process.env.CORS_ALLOW_ORIGINS
@@ -0,0 +1,15 @@
1
+ :root {
2
+ color-scheme: light;
3
+ }
4
+
5
+ * {
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ body {
10
+ margin: 0;
11
+ padding: 0;
12
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
13
+ background: #fafafa;
14
+ color: #111;
15
+ }
@@ -0,0 +1,16 @@
1
+ import './globals.css';
2
+ import type { Metadata } from 'next';
3
+ import { ReactNode } from 'react';
4
+
5
+ export const metadata: Metadata = {
6
+ title: 'xplat-web',
7
+ description: 'Next.js + xplat-back starter',
8
+ };
9
+
10
+ export default function RootLayout({ children }: { children: ReactNode }) {
11
+ return (
12
+ <html lang="en">
13
+ <body>{children}</body>
14
+ </html>
15
+ );
16
+ }
@@ -0,0 +1,12 @@
1
+ export default function Home() {
2
+ return (
3
+ <main style={{ padding: '24px', fontFamily: 'sans-serif' }}>
4
+ <h1>Next.js + xplat-back</h1>
5
+ <p>프론트(Next.js)와 백엔드(xplat-back)를 한 프로젝트에서 시작합니다.</p>
6
+ <ul>
7
+ <li>백엔드: <code>npm run dev</code> (루트)</li>
8
+ <li>프론트: <code>cd web && npm run dev</code></li>
9
+ </ul>
10
+ </main>
11
+ );
12
+ }
@@ -0,0 +1,5 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/basic-features/typescript for more information.
@@ -0,0 +1,9 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ reactStrictMode: true,
4
+ experimental: {
5
+ typedRoutes: true,
6
+ },
7
+ };
8
+
9
+ module.exports = nextConfig;
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "xplat-web",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "next": "14.2.5",
13
+ "react": "18.3.1",
14
+ "react-dom": "18.3.1"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^20.10.0",
18
+ "@types/react": "^18.2.64",
19
+ "@types/react-dom": "^18.2.21",
20
+ "typescript": "^5.3.3",
21
+ "eslint": "^8.57.0",
22
+ "eslint-config-next": "14.2.5"
23
+ }
24
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": false,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "noEmit": true,
10
+ "esModuleInterop": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "node",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "preserve",
16
+ "incremental": true,
17
+ "paths": {
18
+ "@/*": ["./*"]
19
+ }
20
+ },
21
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
22
+ "exclude": ["node_modules"]
23
+ }
@@ -11,7 +11,10 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "express": "^4.18.2",
14
- "xplat-back": "^0.1.0"
14
+ "dotenv": "^16.4.5",
15
+ "typeorm": "^0.3.20",
16
+ "mysql2": "^3.9.7",
17
+ "xplat-back": "^0.5.1"
15
18
  },
16
19
  "devDependencies": {
17
20
  "@types/express": "^4.17.21",
@@ -0,0 +1,92 @@
1
+ import { DataSource, DataSourceOptions } from 'typeorm';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import dotenv from 'dotenv';
5
+
6
+ dotenv.config();
7
+
8
+ /**
9
+ * 프로젝트 엔티티를 동적으로 로딩합니다.
10
+ * 필요에 따라 경로를 조정하거나 직접 배열로 지정해 주세요.
11
+ */
12
+ const loadEntities = (dir: string) => {
13
+ const entities: Function[] = [];
14
+ if (!fs.existsSync(dir)) return entities;
15
+
16
+ const files = fs.readdirSync(dir);
17
+ for (const file of files) {
18
+ if (file.endsWith('.ts') || file.endsWith('.js')) {
19
+ const entity = require(path.join(dir, file));
20
+ for (const key in entity) {
21
+ entities.push(entity[key]);
22
+ }
23
+ }
24
+ }
25
+ return entities;
26
+ };
27
+
28
+ const connectionInfo = (target: string): DataSourceOptions => {
29
+ const port =
30
+ Number(process.env[`${target}_MYSQL_PORT`]) ||
31
+ Number(process.env[`${target}_PORT`]) ||
32
+ 3306;
33
+
34
+ return {
35
+ type: 'mysql',
36
+ // 개발용 스키마 자동 동기화 (운영에서는 DB_SYNC=false 권장)
37
+ synchronize: process.env.DB_SYNC === 'true',
38
+ logging: process.env.NODE_ENV === 'production' ? ['error'] : ['query', 'error'],
39
+ host: process.env[`${target}_HOST`] || 'localhost',
40
+ port,
41
+ username: process.env[`${target}_USER`] || 'root',
42
+ password: process.env[`${target}_PASSWORD`] || '',
43
+ database: process.env[`${target}_DATABASE`] || 'database',
44
+ charset: 'utf8mb4',
45
+ timezone: '+09:00',
46
+ extra: {
47
+ connectionLimit: 50,
48
+ connectTimeout: 60000,
49
+ waitForConnections: true,
50
+ idleTimeout: 300000,
51
+ acquireTimeout: 60000,
52
+ reconnect: true,
53
+ dateStrings: true,
54
+ },
55
+ };
56
+ };
57
+
58
+ export const mainDB = new DataSource({
59
+ ...connectionInfo('DB'),
60
+ // 필요 시 엔티티 경로를 변경하거나 직접 배열로 설정하세요.
61
+ entities: loadEntities(path.join(__dirname, '..', '..', 'entities')),
62
+ });
63
+
64
+ /**
65
+ * 데이터베이스 연결을 초기화합니다.
66
+ */
67
+ export const initializeDatabase = async () => {
68
+ if (!mainDB.isInitialized) {
69
+ await mainDB.initialize();
70
+ console.log('✅ Database connection initialized');
71
+ return;
72
+ }
73
+
74
+ try {
75
+ await mainDB.query('SELECT 1');
76
+ } catch (error) {
77
+ console.error('Connection lost. Reconnecting...', error);
78
+ await mainDB.destroy();
79
+ await mainDB.initialize();
80
+ }
81
+ };
82
+
83
+ /**
84
+ * 엔티티 기준 스키마 동기화 (DB_SYNC=true 일 때만 실행 권장)
85
+ */
86
+ export const syncSchema = async () => {
87
+ if (!mainDB.isInitialized) {
88
+ await mainDB.initialize();
89
+ }
90
+ await mainDB.synchronize();
91
+ console.log('✅ Database schema synchronized from entities');
92
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * CommerceModule
3
+ * - Store/Admin API 분리 구조를 제공하는 최상위 모듈
4
+ * - 도메인 모듈은 하위 todos에서 점진적으로 추가됩니다.
5
+ */
6
+ export declare class CommerceModule {
7
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.CommerceModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const admin_api_module_1 = require("./modules/admin/admin-api.module");
12
+ const store_api_module_1 = require("./modules/store/store-api.module");
13
+ /**
14
+ * CommerceModule
15
+ * - Store/Admin API 분리 구조를 제공하는 최상위 모듈
16
+ * - 도메인 모듈은 하위 todos에서 점진적으로 추가됩니다.
17
+ */
18
+ let CommerceModule = class CommerceModule {
19
+ };
20
+ exports.CommerceModule = CommerceModule;
21
+ exports.CommerceModule = CommerceModule = __decorate([
22
+ (0, common_1.Module)({
23
+ imports: [store_api_module_1.StoreApiModule, admin_api_module_1.AdminApiModule],
24
+ })
25
+ ], CommerceModule);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Admin API (v1)
3
+ * - 운영/머천트용 엔드포인트를 제공합니다.
4
+ * - prefix는 generated template에서 /admin/v1 로 고정될 예정이며,
5
+ * 컨트롤러는 이후 도메인별로 확장합니다.
6
+ */
7
+ export declare class AdminApiModule {
8
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.AdminApiModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const admin_health_controller_1 = require("./controllers/admin-health.controller");
12
+ /**
13
+ * Admin API (v1)
14
+ * - 운영/머천트용 엔드포인트를 제공합니다.
15
+ * - prefix는 generated template에서 /admin/v1 로 고정될 예정이며,
16
+ * 컨트롤러는 이후 도메인별로 확장합니다.
17
+ */
18
+ let AdminApiModule = class AdminApiModule {
19
+ };
20
+ exports.AdminApiModule = AdminApiModule;
21
+ exports.AdminApiModule = AdminApiModule = __decorate([
22
+ (0, common_1.Module)({
23
+ controllers: [admin_health_controller_1.AdminHealthController],
24
+ })
25
+ ], AdminApiModule);
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Admin Health
3
+ * - 스캐폴딩/연결 확인용 최소 엔드포인트
4
+ */
5
+ export declare class AdminHealthController {
6
+ health(): {
7
+ ok: boolean;
8
+ scope: string;
9
+ timestamp: string;
10
+ };
11
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.AdminHealthController = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ /**
15
+ * Admin Health
16
+ * - 스캐폴딩/연결 확인용 최소 엔드포인트
17
+ */
18
+ let AdminHealthController = class AdminHealthController {
19
+ health() {
20
+ return {
21
+ ok: true,
22
+ scope: 'admin',
23
+ timestamp: new Date().toISOString(),
24
+ };
25
+ }
26
+ };
27
+ exports.AdminHealthController = AdminHealthController;
28
+ __decorate([
29
+ (0, common_1.Get)('health'),
30
+ __metadata("design:type", Function),
31
+ __metadata("design:paramtypes", []),
32
+ __metadata("design:returntype", void 0)
33
+ ], AdminHealthController.prototype, "health", null);
34
+ exports.AdminHealthController = AdminHealthController = __decorate([
35
+ (0, common_1.Controller)()
36
+ ], AdminHealthController);
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Store Health
3
+ * - 스캐폴딩/연결 확인용 최소 엔드포인트
4
+ */
5
+ export declare class StoreHealthController {
6
+ health(): {
7
+ ok: boolean;
8
+ scope: string;
9
+ timestamp: string;
10
+ };
11
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.StoreHealthController = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ /**
15
+ * Store Health
16
+ * - 스캐폴딩/연결 확인용 최소 엔드포인트
17
+ */
18
+ let StoreHealthController = class StoreHealthController {
19
+ health() {
20
+ return {
21
+ ok: true,
22
+ scope: 'store',
23
+ timestamp: new Date().toISOString(),
24
+ };
25
+ }
26
+ };
27
+ exports.StoreHealthController = StoreHealthController;
28
+ __decorate([
29
+ (0, common_1.Get)('health'),
30
+ __metadata("design:type", Function),
31
+ __metadata("design:paramtypes", []),
32
+ __metadata("design:returntype", void 0)
33
+ ], StoreHealthController.prototype, "health", null);
34
+ exports.StoreHealthController = StoreHealthController = __decorate([
35
+ (0, common_1.Controller)()
36
+ ], StoreHealthController);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Store API (v1)
3
+ * - 공개/고객용 엔드포인트를 제공합니다.
4
+ * - prefix는 generated template에서 /store/v1 로 고정될 예정이며,
5
+ * 컨트롤러는 이후 도메인별로 확장합니다.
6
+ */
7
+ export declare class StoreApiModule {
8
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.StoreApiModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const store_health_controller_1 = require("./controllers/store-health.controller");
12
+ /**
13
+ * Store API (v1)
14
+ * - 공개/고객용 엔드포인트를 제공합니다.
15
+ * - prefix는 generated template에서 /store/v1 로 고정될 예정이며,
16
+ * 컨트롤러는 이후 도메인별로 확장합니다.
17
+ */
18
+ let StoreApiModule = class StoreApiModule {
19
+ };
20
+ exports.StoreApiModule = StoreApiModule;
21
+ exports.StoreApiModule = StoreApiModule = __decorate([
22
+ (0, common_1.Module)({
23
+ controllers: [store_health_controller_1.StoreHealthController],
24
+ })
25
+ ], StoreApiModule);
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 커머스 플랫폼 공통 타입/계약 (Medusa 스타일 참고)
3
+ */
4
+ export type ApiScope = 'store' | 'admin';
5
+ export interface ApiVersioning {
6
+ storePrefix: string;
7
+ adminPrefix: string;
8
+ }
9
+ export interface CommerceCoreConfig {
10
+ versioning?: Partial<ApiVersioning>;
11
+ }
12
+ /**
13
+ * 확장 포인트: 이벤트 버스
14
+ * - 초기 구현은 간단한 in-memory pub/sub로 시작하고,
15
+ * 이후 Kafka/SQS/Redis 등으로 교체 가능하도록 포트만 정의합니다.
16
+ */
17
+ export interface EventBus {
18
+ publish<TPayload>(eventName: string, payload: TPayload): Promise<void>;
19
+ subscribe<TPayload>(eventName: string, handler: (payload: TPayload) => Promise<void> | void): () => void;
20
+ }
21
+ /**
22
+ * 확장 포인트: Job/Queue
23
+ */
24
+ export interface JobQueue {
25
+ enqueue<TPayload>(jobName: string, payload: TPayload, opts?: Record<string, any>): Promise<string>;
26
+ }
27
+ /**
28
+ * 확장 포인트: Hook
29
+ * - 특정 도메인 흐름(예: cart.updated, order.placed) 전/후 처리 훅
30
+ */
31
+ export interface HookRunner {
32
+ run<TContext>(hookName: string, ctx: TContext): Promise<TContext>;
33
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * 커머스 플랫폼 공통 타입/계약 (Medusa 스타일 참고)
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -8,6 +8,7 @@ import { StorageModule } from '../modules/storage';
8
8
  import { ORMModule } from '../modules/orm';
9
9
  import { PaymentModule } from '../modules/payment';
10
10
  import { ShippingModule } from '../modules/shipping';
11
+ import { CommerceModule } from '../modules/commerce';
11
12
  /**
12
13
  * XplatSystem - Singleton 패턴 기반 Framework 인스턴스
13
14
  *
@@ -25,6 +26,7 @@ export declare class XplatSystem {
25
26
  readonly orm: ORMModule;
26
27
  readonly payment: PaymentModule;
27
28
  readonly shipping: ShippingModule;
29
+ readonly commerce: CommerceModule;
28
30
  /**
29
31
  * Private constructor (Singleton 패턴)
30
32
  */
@@ -11,6 +11,7 @@ const storage_1 = require("../modules/storage");
11
11
  const orm_1 = require("../modules/orm");
12
12
  const payment_1 = require("../modules/payment");
13
13
  const shipping_1 = require("../modules/shipping");
14
+ const commerce_1 = require("../modules/commerce");
14
15
  /**
15
16
  * XplatSystem - Singleton 패턴 기반 Framework 인스턴스
16
17
  *
@@ -31,6 +32,7 @@ class XplatSystem {
31
32
  this.orm = new orm_1.ORMModule();
32
33
  this.payment = new payment_1.PaymentModule();
33
34
  this.shipping = new shipping_1.ShippingModule();
35
+ this.commerce = new commerce_1.CommerceModule();
34
36
  }
35
37
  /**
36
38
  * Singleton 인스턴스 반환
@@ -0,0 +1,15 @@
1
+ import { CommerceCoreConfig } from '../../commerce/types';
2
+ /**
3
+ * CommerceModule (xplatSystem용 래퍼)
4
+ * - Nest 모듈(CommerceModule)을 직접 노출하기 전에,
5
+ * 싱글턴 접근 패턴(`xplatSystem.commerce`)을 유지하기 위한 최소 래퍼입니다.
6
+ * - 실제 동작 구현(이벤트/잡/도메인 서비스)은 후속 todo에서 확장합니다.
7
+ */
8
+ export declare class CommerceModule {
9
+ private config;
10
+ constructor(config?: CommerceCoreConfig);
11
+ getVersioning(): {
12
+ storePrefix: string;
13
+ adminPrefix: string;
14
+ };
15
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CommerceModule = void 0;
4
+ /**
5
+ * CommerceModule (xplatSystem용 래퍼)
6
+ * - Nest 모듈(CommerceModule)을 직접 노출하기 전에,
7
+ * 싱글턴 접근 패턴(`xplatSystem.commerce`)을 유지하기 위한 최소 래퍼입니다.
8
+ * - 실제 동작 구현(이벤트/잡/도메인 서비스)은 후속 todo에서 확장합니다.
9
+ */
10
+ class CommerceModule {
11
+ constructor(config = {}) {
12
+ this.config = config;
13
+ }
14
+ getVersioning() {
15
+ return {
16
+ storePrefix: this.config.versioning?.storePrefix ?? '/store/v1',
17
+ adminPrefix: this.config.versioning?.adminPrefix ?? '/admin/v1',
18
+ };
19
+ }
20
+ }
21
+ exports.CommerceModule = CommerceModule;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hamjimin/xplat-back",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Express + TypeScript 백엔드 스캐폴딩 도구 (zium-backend 기반)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,7 +10,6 @@
10
10
  "bin": {
11
11
  "xplat-back": "./bin/xplat-back"
12
12
  },
13
-
14
13
  "files": [
15
14
  "dist",
16
15
  "bin",
@@ -36,13 +35,20 @@
36
35
  "author": "xplat",
37
36
  "license": "ISC",
38
37
  "dependencies": {
39
- "express": "^4.18.2",
38
+ "@nestjs/common": "^10.4.15",
39
+ "@nestjs/core": "^10.4.15",
40
+ "@nestjs/platform-express": "^10.4.15",
41
+ "class-transformer": "^0.5.1",
42
+ "class-validator": "^0.14.1",
40
43
  "cookie-parser": "^1.4.6",
41
44
  "cors": "^2.8.5",
45
+ "express": "^4.18.2",
42
46
  "jsonwebtoken": "^9.0.2",
43
47
  "bcrypt": "^5.1.1",
44
48
  "crypto-js": "^4.2.0",
45
49
  "lodash": "^4.17.21",
50
+ "reflect-metadata": "^0.2.2",
51
+ "rxjs": "^7.8.1",
46
52
  "uuid": "^9.0.1",
47
53
  "@aws-sdk/client-s3": "^3.922.0",
48
54
  "@aws-sdk/s3-request-presigner": "^3.922.0"