@thinkdata/cli 0.1.0 → 0.2.1
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/dist/bin/thinkdata.js +24 -4
- package/dist/bin/thinkdata.js.map +1 -1
- package/dist/commands/auth.d.ts +9 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +61 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/compare.d.ts +1 -0
- package/dist/commands/compare.d.ts.map +1 -1
- package/dist/commands/compare.js +2 -2
- package/dist/commands/compare.js.map +1 -1
- package/dist/commands/extract.d.ts +1 -0
- package/dist/commands/extract.d.ts.map +1 -1
- package/dist/commands/extract.js +40 -10
- package/dist/commands/extract.js.map +1 -1
- package/dist/commands/serve.d.ts +9 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +348 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/extractors/index.d.ts.map +1 -1
- package/dist/extractors/index.js +7 -2
- package/dist/extractors/index.js.map +1 -1
- package/dist/extractors/mssql.d.ts +14 -0
- package/dist/extractors/mssql.d.ts.map +1 -0
- package/dist/extractors/mssql.js +234 -0
- package/dist/extractors/mssql.js.map +1 -0
- package/dist/extractors/mysql.d.ts +4 -2
- package/dist/extractors/mysql.d.ts.map +1 -1
- package/dist/extractors/mysql.js +30 -8
- package/dist/extractors/mysql.js.map +1 -1
- package/dist/extractors/oracle.d.ts +13 -0
- package/dist/extractors/oracle.d.ts.map +1 -0
- package/dist/extractors/oracle.js +189 -0
- package/dist/extractors/oracle.js.map +1 -0
- package/dist/extractors/postgres.d.ts +6 -3
- package/dist/extractors/postgres.d.ts.map +1 -1
- package/dist/extractors/postgres.js +63 -13
- package/dist/extractors/postgres.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +16 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -6
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* serve 명령 — CLI Agent HTTP 서버
|
|
3
|
+
*
|
|
4
|
+
* 브라우저(ThinkERD)에서 직접 접근하는 로컬 HTTP 서버.
|
|
5
|
+
* 데이터가 외부 서버를 경유하지 않고 localhost에서만 처리됩니다.
|
|
6
|
+
*
|
|
7
|
+
* 사용법:
|
|
8
|
+
* npx @thinkdata/cli serve
|
|
9
|
+
* npx @thinkdata/cli serve --port 3100
|
|
10
|
+
*/
|
|
11
|
+
import { createServer } from 'node:http';
|
|
12
|
+
import { createExtractor } from '../extractors/index.js';
|
|
13
|
+
import { convertToERD } from '../converter/schemaToERD.js';
|
|
14
|
+
import { compareSchemas } from './compare.js';
|
|
15
|
+
const DEFAULT_PORT = 3100;
|
|
16
|
+
/**
|
|
17
|
+
* JSON 바디 파서
|
|
18
|
+
*/
|
|
19
|
+
async function parseBody(req) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
let body = '';
|
|
22
|
+
req.on('data', chunk => { body += chunk; });
|
|
23
|
+
req.on('end', () => {
|
|
24
|
+
try {
|
|
25
|
+
resolve(body ? JSON.parse(body) : {});
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
reject(new Error('Invalid JSON body'));
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
req.on('error', reject);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* CORS 헤더 설정
|
|
36
|
+
*/
|
|
37
|
+
function setCorsHeaders(res) {
|
|
38
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
39
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
40
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
41
|
+
res.setHeader('Access-Control-Max-Age', '86400');
|
|
42
|
+
// Private Network Access(PNA): 공개 출처(https 배포 앱) → localhost 요청 시
|
|
43
|
+
// Chrome이 프리플라이트에서 이 헤더를 요구한다. 없으면 연결이 차단된다.
|
|
44
|
+
res.setHeader('Access-Control-Allow-Private-Network', 'true');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* JSON 응답 헬퍼
|
|
48
|
+
*/
|
|
49
|
+
function jsonResponse(res, status, data) {
|
|
50
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
51
|
+
res.end(JSON.stringify(data));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* ConnectionConfig 변환 (요청 바디 → CLI 타입)
|
|
55
|
+
*/
|
|
56
|
+
function toConnectionConfig(body) {
|
|
57
|
+
const engineMap = {
|
|
58
|
+
postgresql: 'postgres',
|
|
59
|
+
postgres: 'postgres',
|
|
60
|
+
mysql: 'mysql',
|
|
61
|
+
mariadb: 'mysql',
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
type: engineMap[body.engine] || body.engine || 'postgres',
|
|
65
|
+
host: body.host || 'localhost',
|
|
66
|
+
port: body.port || 5432,
|
|
67
|
+
database: body.database,
|
|
68
|
+
user: body.username || body.user,
|
|
69
|
+
password: body.password || '',
|
|
70
|
+
schema: body.schema,
|
|
71
|
+
ssl: body.ssl || false,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 라우트 핸들러
|
|
76
|
+
*/
|
|
77
|
+
const routes = {
|
|
78
|
+
// ── Health Check ──
|
|
79
|
+
'GET /health': async (_req, res) => {
|
|
80
|
+
jsonResponse(res, 200, {
|
|
81
|
+
status: 'ok',
|
|
82
|
+
agent: '@thinkdata/cli',
|
|
83
|
+
version: '0.2.1',
|
|
84
|
+
timestamp: new Date().toISOString(),
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
// ── 연결 테스트 ──
|
|
88
|
+
'POST /test-connection': async (req, res) => {
|
|
89
|
+
const body = await parseBody(req);
|
|
90
|
+
if (!body.database || !body.username) {
|
|
91
|
+
jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const config = toConnectionConfig(body);
|
|
95
|
+
try {
|
|
96
|
+
const extractor = createExtractor(config.type);
|
|
97
|
+
const version = await extractor.testConnection(config);
|
|
98
|
+
// 비밀번호 즉시 폐기
|
|
99
|
+
config.password = '';
|
|
100
|
+
jsonResponse(res, 200, {
|
|
101
|
+
success: true,
|
|
102
|
+
engine: config.type,
|
|
103
|
+
version,
|
|
104
|
+
host: config.host,
|
|
105
|
+
database: config.database,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
config.password = '';
|
|
110
|
+
jsonResponse(res, 400, {
|
|
111
|
+
success: false,
|
|
112
|
+
error: err.message || 'Connection failed',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
// ── 스키마 목록 조회 ──
|
|
117
|
+
'POST /list-schemas': async (req, res) => {
|
|
118
|
+
const body = await parseBody(req);
|
|
119
|
+
if (!body.database || !body.username) {
|
|
120
|
+
jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const config = toConnectionConfig(body);
|
|
124
|
+
try {
|
|
125
|
+
const extractor = createExtractor(config.type);
|
|
126
|
+
const schemas = extractor.listSchemas ? await extractor.listSchemas(config) : [];
|
|
127
|
+
config.password = '';
|
|
128
|
+
jsonResponse(res, 200, { success: true, schemas });
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
config.password = '';
|
|
132
|
+
jsonResponse(res, 400, { success: false, error: err.message || 'Failed to list schemas' });
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
// ── 스키마 추출 ──
|
|
136
|
+
'POST /extract-schema': async (req, res) => {
|
|
137
|
+
const body = await parseBody(req);
|
|
138
|
+
if (!body.database || !body.username) {
|
|
139
|
+
jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const config = toConnectionConfig(body);
|
|
143
|
+
try {
|
|
144
|
+
const extractor = createExtractor(config.type);
|
|
145
|
+
const schema = await extractor.extract(config);
|
|
146
|
+
// 비밀번호 즉시 폐기
|
|
147
|
+
config.password = '';
|
|
148
|
+
// ERD 포맷으로 변환 (옵션)
|
|
149
|
+
if (body.format === 'erd') {
|
|
150
|
+
const erd = convertToERD(schema);
|
|
151
|
+
jsonResponse(res, 200, {
|
|
152
|
+
success: true,
|
|
153
|
+
format: 'erd',
|
|
154
|
+
data: erd,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
jsonResponse(res, 200, {
|
|
159
|
+
success: true,
|
|
160
|
+
format: 'raw',
|
|
161
|
+
data: schema,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
config.password = '';
|
|
167
|
+
jsonResponse(res, 500, {
|
|
168
|
+
success: false,
|
|
169
|
+
error: err.message || 'Schema extraction failed',
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
// ── 스키마 추출 (Streaming SSE) ──
|
|
174
|
+
'POST /extract-stream': async (req, res) => {
|
|
175
|
+
const body = await parseBody(req);
|
|
176
|
+
if (!body.database || !body.username) {
|
|
177
|
+
jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const config = toConnectionConfig(body);
|
|
181
|
+
// SSE 헤더 설정
|
|
182
|
+
res.writeHead(200, {
|
|
183
|
+
'Content-Type': 'text/event-stream',
|
|
184
|
+
'Cache-Control': 'no-cache',
|
|
185
|
+
'Connection': 'keep-alive',
|
|
186
|
+
});
|
|
187
|
+
const sendEvent = (event, data) => {
|
|
188
|
+
res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
|
|
189
|
+
};
|
|
190
|
+
try {
|
|
191
|
+
const extractor = createExtractor(config.type);
|
|
192
|
+
const schema = await extractor.extract(config, (msg) => {
|
|
193
|
+
sendEvent('progress', { message: msg });
|
|
194
|
+
});
|
|
195
|
+
config.password = '';
|
|
196
|
+
let responseData = schema;
|
|
197
|
+
if (body.format === 'erd') {
|
|
198
|
+
responseData = convertToERD(schema);
|
|
199
|
+
}
|
|
200
|
+
sendEvent('complete', {
|
|
201
|
+
success: true,
|
|
202
|
+
format: body.format === 'erd' ? 'erd' : 'raw',
|
|
203
|
+
data: responseData,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
config.password = '';
|
|
208
|
+
sendEvent('error', {
|
|
209
|
+
success: false,
|
|
210
|
+
error: err.message || 'Schema extraction failed',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
res.end();
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
// ── 스키마 비교 (드리프트 감지) ──
|
|
218
|
+
'POST /compare-schema': async (req, res) => {
|
|
219
|
+
const body = await parseBody(req);
|
|
220
|
+
if (!body.database || !body.username) {
|
|
221
|
+
jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (!body.model) {
|
|
225
|
+
jsonResponse(res, 400, { error: 'model (ERD 스키마)은 필수입니다.' });
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const config = toConnectionConfig(body);
|
|
229
|
+
try {
|
|
230
|
+
const extractor = createExtractor(config.type);
|
|
231
|
+
const dbSchema = await extractor.extract(config);
|
|
232
|
+
// 비밀번호 즉시 폐기
|
|
233
|
+
config.password = '';
|
|
234
|
+
// 모델 스키마와 비교
|
|
235
|
+
const modelSchema = body.model;
|
|
236
|
+
const diff = compareSchemas(dbSchema, modelSchema);
|
|
237
|
+
jsonResponse(res, 200, {
|
|
238
|
+
success: true,
|
|
239
|
+
diff,
|
|
240
|
+
summary: {
|
|
241
|
+
added: diff.added.length,
|
|
242
|
+
removed: diff.removed.length,
|
|
243
|
+
modified: diff.modified.length,
|
|
244
|
+
fkDiffs: diff.fkDiffs.length,
|
|
245
|
+
hasChanges: diff.added.length > 0 || diff.removed.length > 0 ||
|
|
246
|
+
diff.modified.length > 0 || diff.fkDiffs.length > 0,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
config.password = '';
|
|
252
|
+
jsonResponse(res, 500, {
|
|
253
|
+
success: false,
|
|
254
|
+
error: err.message || 'Schema comparison failed',
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
// ── DataOps 통계 지표 수집 ──
|
|
259
|
+
'POST /dataops-stats': async (req, res) => {
|
|
260
|
+
const body = await parseBody(req);
|
|
261
|
+
if (!body.database || !body.username) {
|
|
262
|
+
jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const config = toConnectionConfig(body);
|
|
266
|
+
try {
|
|
267
|
+
const extractor = createExtractor(config.type);
|
|
268
|
+
if (!extractor.getStats) {
|
|
269
|
+
jsonResponse(res, 400, {
|
|
270
|
+
success: false,
|
|
271
|
+
error: `DataOps 통계 수집은 현재 ${config.type} 엔진을 지원하지 않습니다.`,
|
|
272
|
+
});
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const stats = await extractor.getStats(config);
|
|
276
|
+
// 비밀번호 즉시 폐기
|
|
277
|
+
config.password = '';
|
|
278
|
+
jsonResponse(res, 200, {
|
|
279
|
+
success: true,
|
|
280
|
+
data: stats,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
config.password = '';
|
|
285
|
+
jsonResponse(res, 500, {
|
|
286
|
+
success: false,
|
|
287
|
+
error: err.message || 'Failed to fetch DataOps stats',
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* HTTP 서버 시작
|
|
294
|
+
*/
|
|
295
|
+
export async function serveCommand(options) {
|
|
296
|
+
const port = options.port || DEFAULT_PORT;
|
|
297
|
+
const server = createServer(async (req, res) => {
|
|
298
|
+
setCorsHeaders(res);
|
|
299
|
+
// CORS Preflight
|
|
300
|
+
if (req.method === 'OPTIONS') {
|
|
301
|
+
res.writeHead(204);
|
|
302
|
+
res.end();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const routeKey = `${req.method} ${req.url?.split('?')[0]}`;
|
|
306
|
+
const handler = routes[routeKey];
|
|
307
|
+
if (handler) {
|
|
308
|
+
try {
|
|
309
|
+
await handler(req, res);
|
|
310
|
+
}
|
|
311
|
+
catch (err) {
|
|
312
|
+
console.error(`[Agent] Error handling ${routeKey}:`, err);
|
|
313
|
+
jsonResponse(res, 500, { error: 'Internal server error' });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
jsonResponse(res, 404, { error: `Not found: ${routeKey}` });
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
server.listen(port, '127.0.0.1', () => {
|
|
321
|
+
console.log();
|
|
322
|
+
console.log(' ┌─────────────────────────────────────────────┐');
|
|
323
|
+
console.log(' │ │');
|
|
324
|
+
console.log(` │ 🚀 ThinkERD Agent running on port ${port} │`);
|
|
325
|
+
console.log(' │ │');
|
|
326
|
+
console.log(` │ Health: http://localhost:${port}/health │`);
|
|
327
|
+
console.log(' │ Bound: 127.0.0.1 (local only) │');
|
|
328
|
+
console.log(' │ │');
|
|
329
|
+
console.log(' │ ✅ Data never leaves your machine │');
|
|
330
|
+
console.log(' │ ✅ Passwords stay in process memory only │');
|
|
331
|
+
console.log(' │ │');
|
|
332
|
+
console.log(' │ Press Ctrl+C to stop │');
|
|
333
|
+
console.log(' │ │');
|
|
334
|
+
console.log(' └─────────────────────────────────────────────┘');
|
|
335
|
+
console.log();
|
|
336
|
+
});
|
|
337
|
+
// Graceful shutdown
|
|
338
|
+
process.on('SIGINT', () => {
|
|
339
|
+
console.log('\n 👋 Agent stopped.');
|
|
340
|
+
server.close();
|
|
341
|
+
process.exit(0);
|
|
342
|
+
});
|
|
343
|
+
process.on('SIGTERM', () => {
|
|
344
|
+
server.close();
|
|
345
|
+
process.exit(0);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extractors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extractors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAM5C,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAWrE;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAe7D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAwBvE"}
|
package/dist/extractors/index.js
CHANGED
|
@@ -5,6 +5,8 @@ export { PostgresExtractor } from './postgres.js';
|
|
|
5
5
|
export { MySQLExtractor } from './mysql.js';
|
|
6
6
|
import { PostgresExtractor } from './postgres.js';
|
|
7
7
|
import { MySQLExtractor } from './mysql.js';
|
|
8
|
+
import { OracleExtractor } from './oracle.js';
|
|
9
|
+
import { MSSQLExtractor } from './mssql.js';
|
|
8
10
|
/** DBMS별 기본 포트 */
|
|
9
11
|
const DEFAULT_PORTS = {
|
|
10
12
|
postgres: 5432,
|
|
@@ -24,9 +26,12 @@ export function createExtractor(type) {
|
|
|
24
26
|
case 'mysql':
|
|
25
27
|
case 'mariadb':
|
|
26
28
|
return new MySQLExtractor();
|
|
27
|
-
|
|
29
|
+
case 'oracle':
|
|
30
|
+
return new OracleExtractor();
|
|
31
|
+
case 'mssql':
|
|
32
|
+
return new MSSQLExtractor();
|
|
28
33
|
default:
|
|
29
|
-
throw new Error(`지원하지 않는 DBMS
|
|
34
|
+
throw new Error(`지원하지 않는 DBMS 입니다: ${type}. 현재 지원: postgres, mysql, oracle, mssql`);
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extractors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,kBAAkB;AAClB,MAAM,aAAa,GAA2B;IAC1C,QAAQ,EAAE,IAAI;IACd,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,CAAC;CACZ,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IACxC,QAAQ,IAAI,EAAE,CAAC;QACX,KAAK,UAAU,CAAC;QAChB,KAAK,YAAY;YACb,OAAO,IAAI,iBAAiB,EAAE,CAAC;QACnC,KAAK,OAAO,CAAC;QACb,KAAK,SAAS;YACV,OAAO,IAAI,cAAc,EAAE,CAAC;QAChC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extractors/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SchemaExtractor, ConnectionConfig, ExtractedSchema, ExtractProgressCallback } from '../types.js';
|
|
2
|
+
export declare class MSSQLExtractor implements SchemaExtractor {
|
|
3
|
+
testConnection(config: ConnectionConfig): Promise<string>;
|
|
4
|
+
extract(config: ConnectionConfig, onProgress?: ExtractProgressCallback): Promise<ExtractedSchema>;
|
|
5
|
+
private createConnection;
|
|
6
|
+
private getVersion;
|
|
7
|
+
private getTables;
|
|
8
|
+
private getPrimaryKeys;
|
|
9
|
+
private getForeignKeys;
|
|
10
|
+
private getIndexes;
|
|
11
|
+
private buildPKMap;
|
|
12
|
+
private buildIndexMap;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=mssql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mssql.d.ts","sourceRoot":"","sources":["../../src/extractors/mssql.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACR,eAAe,EACf,gBAAgB,EAChB,eAAe,EAKf,uBAAuB,EAC1B,MAAM,aAAa,CAAC;AAErB,qBAAa,cAAe,YAAW,eAAe;IAC5C,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzD,OAAO,CAAC,MAAM,EAAE,gBAAgB,EAAE,UAAU,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,eAAe,CAAC;YA8DzF,gBAAgB;YAehB,UAAU;YAKV,SAAS;YAiDT,cAAc;YAYd,cAAc;YA6Cd,UAAU;IAoBxB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,aAAa;CAmBxB"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL Server (MSSQL) 스키마 추출기
|
|
3
|
+
*/
|
|
4
|
+
import sql from 'mssql';
|
|
5
|
+
export class MSSQLExtractor {
|
|
6
|
+
async testConnection(config) {
|
|
7
|
+
const pool = await this.createConnection(config);
|
|
8
|
+
try {
|
|
9
|
+
const result = await pool.request().query('SELECT @@version AS ver');
|
|
10
|
+
return result.recordset[0].ver;
|
|
11
|
+
}
|
|
12
|
+
finally {
|
|
13
|
+
await pool.close();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async extract(config, onProgress) {
|
|
17
|
+
const pool = await this.createConnection(config);
|
|
18
|
+
const schema = config.schema || 'dbo';
|
|
19
|
+
try {
|
|
20
|
+
onProgress?.(`Connecting to SQL Server at ${config.host}...`);
|
|
21
|
+
onProgress?.(`Fetching server version...`);
|
|
22
|
+
const version = await this.getVersion(pool);
|
|
23
|
+
onProgress?.(`Fetching tables and columns from schema '${schema}'...`);
|
|
24
|
+
const tables = await this.getTables(pool, schema);
|
|
25
|
+
onProgress?.(`Found ${tables.length} tables.`);
|
|
26
|
+
onProgress?.(`Fetching primary keys...`);
|
|
27
|
+
const pks = await this.getPrimaryKeys(pool, schema);
|
|
28
|
+
onProgress?.(`Fetching foreign keys...`);
|
|
29
|
+
const fks = await this.getForeignKeys(pool, schema);
|
|
30
|
+
onProgress?.(`Fetching indexes...`);
|
|
31
|
+
const indexes = await this.getIndexes(pool, schema);
|
|
32
|
+
onProgress?.(`Building relationships and formatting output...`);
|
|
33
|
+
const pkMap = this.buildPKMap(pks);
|
|
34
|
+
const indexMap = this.buildIndexMap(indexes);
|
|
35
|
+
const extractedTables = tables.map((t) => ({
|
|
36
|
+
name: t.table_name,
|
|
37
|
+
schema: schema,
|
|
38
|
+
comment: t.table_comment || undefined,
|
|
39
|
+
columns: t.columns.map((c) => ({
|
|
40
|
+
name: c.COLUMN_NAME,
|
|
41
|
+
type: c.COLUMN_TYPE,
|
|
42
|
+
nullable: c.IS_NULLABLE === 'YES',
|
|
43
|
+
isPK: pkMap.get(t.table_name)?.has(c.COLUMN_NAME) ?? false,
|
|
44
|
+
defaultValue: c.COLUMN_DEFAULT || undefined,
|
|
45
|
+
comment: c.COLUMN_COMMENT || undefined,
|
|
46
|
+
ordinalPosition: c.ORDINAL_POSITION,
|
|
47
|
+
maxLength: c.CHARACTER_MAXIMUM_LENGTH || undefined,
|
|
48
|
+
numericPrecision: c.NUMERIC_PRECISION || undefined,
|
|
49
|
+
numericScale: c.NUMERIC_SCALE || undefined,
|
|
50
|
+
})),
|
|
51
|
+
indexes: indexMap.get(t.table_name) || [],
|
|
52
|
+
}));
|
|
53
|
+
return {
|
|
54
|
+
dbmsType: 'mssql',
|
|
55
|
+
dbmsVersion: version,
|
|
56
|
+
extractedAt: new Date().toISOString(),
|
|
57
|
+
schemaName: schema,
|
|
58
|
+
tables: extractedTables,
|
|
59
|
+
foreignKeys: fks,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
await pool.close();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// ─── Private ─────────────────────────────────────────
|
|
67
|
+
async createConnection(config) {
|
|
68
|
+
return sql.connect({
|
|
69
|
+
server: config.host,
|
|
70
|
+
port: config.port,
|
|
71
|
+
database: config.database,
|
|
72
|
+
user: config.user,
|
|
73
|
+
password: config.password,
|
|
74
|
+
options: {
|
|
75
|
+
encrypt: config.ssl || false,
|
|
76
|
+
trustServerCertificate: !config.ssl,
|
|
77
|
+
},
|
|
78
|
+
connectionTimeout: 10000,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async getVersion(pool) {
|
|
82
|
+
const result = await pool.request().query('SELECT @@version AS ver');
|
|
83
|
+
return result.recordset[0].ver;
|
|
84
|
+
}
|
|
85
|
+
async getTables(pool, schema) {
|
|
86
|
+
const result = await pool.request()
|
|
87
|
+
.input('schema', sql.NVarChar, schema)
|
|
88
|
+
.query(`
|
|
89
|
+
SELECT
|
|
90
|
+
t.TABLE_NAME, c.COLUMN_NAME, c.ORDINAL_POSITION, c.DATA_TYPE as COLUMN_TYPE,
|
|
91
|
+
c.IS_NULLABLE, c.COLUMN_DEFAULT, c.CHARACTER_MAXIMUM_LENGTH,
|
|
92
|
+
c.NUMERIC_PRECISION, c.NUMERIC_SCALE,
|
|
93
|
+
CAST(ep.value AS NVARCHAR(MAX)) AS COLUMN_COMMENT
|
|
94
|
+
FROM information_schema.TABLES t
|
|
95
|
+
JOIN information_schema.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME AND t.TABLE_SCHEMA = c.TABLE_SCHEMA
|
|
96
|
+
LEFT JOIN sys.extended_properties ep ON ep.major_id = OBJECT_ID(t.TABLE_SCHEMA + '.' + t.TABLE_NAME)
|
|
97
|
+
AND ep.minor_id = c.ORDINAL_POSITION AND ep.name = 'MS_Description'
|
|
98
|
+
WHERE t.TABLE_SCHEMA = @schema AND t.TABLE_TYPE = 'BASE TABLE'
|
|
99
|
+
ORDER BY t.TABLE_NAME, c.ORDINAL_POSITION
|
|
100
|
+
`);
|
|
101
|
+
// 테이블 코멘트 가져오기
|
|
102
|
+
const tableComments = await pool.request()
|
|
103
|
+
.input('schema', sql.NVarChar, schema)
|
|
104
|
+
.query(`
|
|
105
|
+
SELECT
|
|
106
|
+
t.TABLE_NAME,
|
|
107
|
+
CAST(ep.value AS NVARCHAR(MAX)) AS TABLE_COMMENT
|
|
108
|
+
FROM information_schema.TABLES t
|
|
109
|
+
JOIN sys.extended_properties ep ON ep.major_id = OBJECT_ID(t.TABLE_SCHEMA + '.' + t.TABLE_NAME)
|
|
110
|
+
WHERE t.TABLE_SCHEMA = @schema AND ep.minor_id = 0 AND ep.name = 'MS_Description'
|
|
111
|
+
`);
|
|
112
|
+
const commentMap = new Map();
|
|
113
|
+
for (const tc of tableComments.recordset) {
|
|
114
|
+
commentMap.set(tc.TABLE_NAME, tc.TABLE_COMMENT);
|
|
115
|
+
}
|
|
116
|
+
const tableMap = new Map();
|
|
117
|
+
for (const col of result.recordset) {
|
|
118
|
+
if (!tableMap.has(col.TABLE_NAME)) {
|
|
119
|
+
tableMap.set(col.TABLE_NAME, {
|
|
120
|
+
table_name: col.TABLE_NAME,
|
|
121
|
+
table_comment: commentMap.get(col.TABLE_NAME) || null,
|
|
122
|
+
columns: [],
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
tableMap.get(col.TABLE_NAME).columns.push(col);
|
|
126
|
+
}
|
|
127
|
+
return Array.from(tableMap.values());
|
|
128
|
+
}
|
|
129
|
+
async getPrimaryKeys(pool, schema) {
|
|
130
|
+
const result = await pool.request()
|
|
131
|
+
.input('schema', sql.NVarChar, schema)
|
|
132
|
+
.query(`
|
|
133
|
+
SELECT c.TABLE_NAME, c.COLUMN_NAME
|
|
134
|
+
FROM information_schema.KEY_COLUMN_USAGE c
|
|
135
|
+
JOIN information_schema.TABLE_CONSTRAINTS t ON c.CONSTRAINT_NAME = t.CONSTRAINT_NAME
|
|
136
|
+
WHERE t.CONSTRAINT_TYPE = 'PRIMARY KEY' AND t.TABLE_SCHEMA = @schema
|
|
137
|
+
`);
|
|
138
|
+
return result.recordset;
|
|
139
|
+
}
|
|
140
|
+
async getForeignKeys(pool, schema) {
|
|
141
|
+
const result = await pool.request()
|
|
142
|
+
.input('schema', sql.NVarChar, schema)
|
|
143
|
+
.query(`
|
|
144
|
+
SELECT
|
|
145
|
+
fk.name AS CONSTRAINT_NAME,
|
|
146
|
+
tp.name AS source_table,
|
|
147
|
+
cp.name AS source_column,
|
|
148
|
+
tr.name AS target_table,
|
|
149
|
+
cr.name AS target_column,
|
|
150
|
+
fk.delete_referential_action_desc AS DELETE_RULE,
|
|
151
|
+
fk.update_referential_action_desc AS UPDATE_RULE
|
|
152
|
+
FROM sys.foreign_keys fk
|
|
153
|
+
INNER JOIN sys.tables tp ON fk.parent_object_id = tp.object_id
|
|
154
|
+
INNER JOIN sys.columns cp ON fk.parent_object_id = cp.object_id
|
|
155
|
+
INNER JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id AND cp.column_id = fkc.parent_column_id
|
|
156
|
+
INNER JOIN sys.tables tr ON fk.referenced_object_id = tr.object_id
|
|
157
|
+
INNER JOIN sys.columns cr ON fk.referenced_object_id = cr.object_id AND cr.column_id = fkc.referenced_column_id
|
|
158
|
+
INNER JOIN sys.schemas s ON tp.schema_id = s.schema_id
|
|
159
|
+
WHERE s.name = @schema
|
|
160
|
+
`);
|
|
161
|
+
return result.recordset.map((r) => {
|
|
162
|
+
const mapRule = (rule) => {
|
|
163
|
+
if (rule === 'NO_ACTION')
|
|
164
|
+
return 'NO ACTION';
|
|
165
|
+
if (rule === 'CASCADE')
|
|
166
|
+
return 'CASCADE';
|
|
167
|
+
if (rule === 'SET_NULL')
|
|
168
|
+
return 'SET NULL';
|
|
169
|
+
if (rule === 'SET_DEFAULT')
|
|
170
|
+
return 'SET DEFAULT';
|
|
171
|
+
return undefined;
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
name: r.CONSTRAINT_NAME,
|
|
175
|
+
sourceTable: r.source_table,
|
|
176
|
+
sourceSchema: schema,
|
|
177
|
+
sourceColumn: r.source_column,
|
|
178
|
+
targetTable: r.target_table,
|
|
179
|
+
targetSchema: schema,
|
|
180
|
+
targetColumn: r.target_column,
|
|
181
|
+
onDelete: mapRule(r.DELETE_RULE),
|
|
182
|
+
onUpdate: mapRule(r.UPDATE_RULE),
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async getIndexes(pool, schema) {
|
|
187
|
+
const result = await pool.request()
|
|
188
|
+
.input('schema', sql.NVarChar, schema)
|
|
189
|
+
.query(`
|
|
190
|
+
SELECT
|
|
191
|
+
t.name AS TABLE_NAME,
|
|
192
|
+
i.name AS INDEX_NAME,
|
|
193
|
+
c.name AS COLUMN_NAME,
|
|
194
|
+
i.is_unique AS UNIQUE_INDEX
|
|
195
|
+
FROM sys.indexes i
|
|
196
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
197
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
198
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
199
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
200
|
+
WHERE s.name = @schema AND i.is_primary_key = 0 AND i.type > 0
|
|
201
|
+
ORDER BY t.name, i.name, ic.key_ordinal
|
|
202
|
+
`);
|
|
203
|
+
return result.recordset;
|
|
204
|
+
}
|
|
205
|
+
buildPKMap(pks) {
|
|
206
|
+
const map = new Map();
|
|
207
|
+
for (const pk of pks) {
|
|
208
|
+
if (!map.has(pk.TABLE_NAME))
|
|
209
|
+
map.set(pk.TABLE_NAME, new Set());
|
|
210
|
+
map.get(pk.TABLE_NAME).add(pk.COLUMN_NAME);
|
|
211
|
+
}
|
|
212
|
+
return map;
|
|
213
|
+
}
|
|
214
|
+
buildIndexMap(indexes) {
|
|
215
|
+
const map = new Map();
|
|
216
|
+
for (const idx of indexes) {
|
|
217
|
+
const key = `${idx.TABLE_NAME}.${idx.INDEX_NAME}`;
|
|
218
|
+
if (!map.has(idx.TABLE_NAME))
|
|
219
|
+
map.set(idx.TABLE_NAME, []);
|
|
220
|
+
let indexObj = map.get(idx.TABLE_NAME).find(i => i.name === idx.INDEX_NAME);
|
|
221
|
+
if (!indexObj) {
|
|
222
|
+
indexObj = {
|
|
223
|
+
name: idx.INDEX_NAME,
|
|
224
|
+
columns: [],
|
|
225
|
+
unique: idx.UNIQUE_INDEX === true || idx.UNIQUE_INDEX === 1,
|
|
226
|
+
};
|
|
227
|
+
map.get(idx.TABLE_NAME).push(indexObj);
|
|
228
|
+
}
|
|
229
|
+
indexObj.columns.push(idx.COLUMN_NAME);
|
|
230
|
+
}
|
|
231
|
+
return map;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=mssql.js.map
|