@gugananuvem/aws-local-simulator 1.0.35 → 1.0.39
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 +662 -658
- package/package.json +10 -3
- package/src/config/default-config.js +71 -0
- package/src/config/env-loader.js +4 -2
- package/src/middleware/authorization.js +271 -0
- package/src/middleware/cloudtrail-logger.js +333 -0
- package/src/middleware/error-handler.js +138 -0
- package/src/middleware/index.js +79 -0
- package/src/middleware/policy-enforcement.js +407 -0
- package/src/server.js +74 -4
- package/src/services/acm/index.js +66 -0
- package/src/services/acm/models/certificate.js +202 -0
- package/src/services/acm/server.js +224 -0
- package/src/services/acm/simulator.js +293 -0
- package/src/services/alb/index.js +104 -0
- package/src/services/alb/models/loadBalancer.js +88 -0
- package/src/services/alb/models/targetGroup.js +128 -0
- package/src/services/alb/server.js +160 -0
- package/src/services/alb/simulator.js +262 -0
- package/src/services/apigateway/index.js +22 -0
- package/src/services/apigateway/server.js +247 -35
- package/src/services/apigateway/simulator.js +780 -26
- package/src/services/asg/index.js +100 -0
- package/src/services/asg/models/autoScalingGroup.js +110 -0
- package/src/services/asg/models/launchTemplate.js +41 -0
- package/src/services/asg/models/scalingPolicy.js +51 -0
- package/src/services/asg/server.js +236 -0
- package/src/services/asg/simulator.js +410 -0
- package/src/services/aurora/cluster-manager.js +731 -0
- package/src/services/aurora/data-api.js +514 -0
- package/src/services/aurora/db-engine.js +270 -0
- package/src/services/aurora/index.js +159 -0
- package/src/services/aurora/rds-api.js +189 -0
- package/src/services/aurora/server.js +213 -0
- package/src/services/aurora/sql-translator.js +334 -0
- package/src/services/cloudformation/index.js +2 -2
- package/src/services/cloudformation/server.js +1 -0
- package/src/services/cloudfront/cache-engine.js +225 -0
- package/src/services/cloudfront/geo-restrictions.js +377 -0
- package/src/services/cloudfront/index.js +220 -0
- package/src/services/cloudfront/models/behavior.js +158 -0
- package/src/services/cloudfront/models/distribution.js +284 -0
- package/src/services/cloudfront/models/index.js +99 -0
- package/src/services/cloudfront/models/invalidation.js +109 -0
- package/src/services/cloudfront/models/origin.js +155 -0
- package/src/services/cloudfront/origin-router.js +552 -0
- package/src/services/cloudfront/origin-validator.js +398 -0
- package/src/services/cloudfront/server.js +512 -0
- package/src/services/cloudfront/signed-url-generator.js +377 -0
- package/src/services/cloudfront/simulator.js +425 -0
- package/src/services/cloudfront/store.js +326 -0
- package/src/services/cloudshell/env-builder.js +27 -0
- package/src/services/cloudshell/index.js +48 -0
- package/src/services/cloudshell/routes.js +60 -0
- package/src/services/cloudshell/session-manager.js +226 -0
- package/src/services/cloudshell/static/.gitkeep +0 -0
- package/src/services/cloudshell/static/index.html +406 -0
- package/src/services/cloudshell/websocket-handler.js +107 -0
- package/src/services/cloudtrail/index.js +2 -3
- package/src/services/cloudtrail/server.js +68 -0
- package/src/services/cognito/server.js +4 -1
- package/src/services/dynamodb/server.js +4 -1
- package/src/services/dynamodb/simulator.js +2 -1
- package/src/services/ec2/index.js +101 -0
- package/src/services/ec2/instanceServer.js +311 -0
- package/src/services/ec2/models/ami.js +39 -0
- package/src/services/ec2/models/instance.js +98 -0
- package/src/services/ec2/models/networkInterface.js +74 -0
- package/src/services/ec2/models/securityGroup.js +71 -0
- package/src/services/ec2/server.js +350 -0
- package/src/services/ec2/simulator.js +436 -0
- package/src/services/eventbridge/server.js +36 -0
- package/src/services/eventbridge/simulator.js +230 -45
- package/src/services/iam/index.js +65 -0
- package/src/services/iam/middleware.js +99 -0
- package/src/services/iam/server.js +331 -0
- package/src/services/iam/simulator.js +1432 -0
- package/src/services/index.js +28 -0
- package/src/services/kinesis-analytics/index.js +105 -0
- package/src/services/kinesis-analytics/models/application.js +325 -0
- package/src/services/kinesis-analytics/models/index.js +12 -0
- package/src/services/kinesis-analytics/server.js +250 -0
- package/src/services/kinesis-analytics/simulator.js +407 -0
- package/src/services/kinesis-analytics/store.js +176 -0
- package/src/services/kinesis-data-streams/index.js +106 -0
- package/src/services/kinesis-data-streams/models/index.js +12 -0
- package/src/services/kinesis-data-streams/models/record.js +70 -0
- package/src/services/kinesis-data-streams/models/stream.js +280 -0
- package/src/services/kinesis-data-streams/server.js +255 -0
- package/src/services/kinesis-data-streams/simulator.js +514 -0
- package/src/services/kinesis-data-streams/store.js +163 -0
- package/src/services/kinesis-firehose/index.js +106 -0
- package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
- package/src/services/kinesis-firehose/models/index.js +10 -0
- package/src/services/kinesis-firehose/server.js +215 -0
- package/src/services/kinesis-firehose/simulator.js +388 -0
- package/src/services/kinesis-firehose/store.js +163 -0
- package/src/services/kms/server.js +4 -1
- package/src/services/lambda/index.js +26 -0
- package/src/services/lambda/server.js +12 -1
- package/src/services/lambda/simulator.js +4 -0
- package/src/services/opensearch/index.js +105 -0
- package/src/services/opensearch/models/document.js +115 -0
- package/src/services/opensearch/models/index.js +73 -0
- package/src/services/opensearch/server.js +310 -0
- package/src/services/opensearch/simulator.js +605 -0
- package/src/services/opensearch/store.js +162 -0
- package/src/services/organizations/control-tower-simulator.js +321 -0
- package/src/services/organizations/control-tower-store.js +182 -0
- package/src/services/organizations/iam-integration.js +413 -0
- package/src/services/organizations/index.js +172 -0
- package/src/services/organizations/models/index.js +13 -0
- package/src/services/organizations/models/invitation.js +48 -0
- package/src/services/organizations/models/organization.js +566 -0
- package/src/services/organizations/models/role.js +244 -0
- package/src/services/organizations/models/scp.js +131 -0
- package/src/services/organizations/server.js +736 -0
- package/src/services/organizations/simulator.js +593 -0
- package/src/services/organizations/store.js +235 -0
- package/src/services/organizations/vpc-integration.js +453 -0
- package/src/services/route53/index.js +67 -0
- package/src/services/route53/models/healthCheck.js +101 -0
- package/src/services/route53/models/hostedZone.js +37 -0
- package/src/services/route53/models/resourceRecord.js +106 -0
- package/src/services/route53/server.js +841 -0
- package/src/services/route53/simulator.js +350 -0
- package/src/services/s3/index.js +62 -0
- package/src/services/s3/presigned-url-generator.js +337 -0
- package/src/services/s3/server.js +231 -0
- package/src/services/s3/simulator.js +551 -4
- package/src/services/sns/index.js +31 -0
- package/src/services/sns/server.js +9 -0
- package/src/services/sqs/index.js +38 -0
- package/src/services/sqs/server.js +57 -4
- package/src/services/sqs/simulator.js +423 -33
- package/src/services/sts/simulator.js +209 -17
- package/src/services/vpc/README.md +459 -0
- package/src/services/vpc/ec2ResponseFormatter.js +431 -0
- package/src/services/vpc/index.js +90 -0
- package/src/services/vpc/models/internetGateway.js +71 -0
- package/src/services/vpc/models/routeTable.js +160 -0
- package/src/services/vpc/models/subnet.js +48 -0
- package/src/services/vpc/models/vpc.js +37 -0
- package/src/services/vpc/models/vpcEndpoint.js +100 -0
- package/src/services/vpc/models/vpcGateway.js +134 -0
- package/src/services/vpc/server.js +836 -0
- package/src/services/vpc/simulator.js +613 -0
- package/src/utils/policy-cache.js +215 -0
- package/aws-config +0 -154
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Aurora Server - HTTP server que roteia requisições para Data API ou RDS Management API.
|
|
5
|
+
*
|
|
6
|
+
* Routing:
|
|
7
|
+
* - x-amz-target header starting with "RDSDataService" → Data API Handler
|
|
8
|
+
* - Body/query Action parameter → RDS Management API Handler
|
|
9
|
+
* - Otherwise → HTTP 400
|
|
10
|
+
*
|
|
11
|
+
* Admin endpoints:
|
|
12
|
+
* - GET /__admin/health
|
|
13
|
+
* - GET /__admin/clusters
|
|
14
|
+
* - GET /__admin/databases
|
|
15
|
+
* - POST /__admin/reset
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const express = require('express');
|
|
19
|
+
const cors = require('cors');
|
|
20
|
+
const logger = require('../../utils/logger');
|
|
21
|
+
|
|
22
|
+
class AuroraServer {
|
|
23
|
+
constructor(port, config) {
|
|
24
|
+
this.port = port;
|
|
25
|
+
this.config = config;
|
|
26
|
+
this.app = express();
|
|
27
|
+
this.server = null;
|
|
28
|
+
|
|
29
|
+
// Handler/manager references set externally by AuroraService
|
|
30
|
+
this.dataApiHandler = null;
|
|
31
|
+
this.rdsApiHandler = null;
|
|
32
|
+
this.clusterManager = null;
|
|
33
|
+
this.dbEngine = null;
|
|
34
|
+
|
|
35
|
+
this.setupMiddlewares();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setupMiddlewares() {
|
|
39
|
+
this.app.use(cors());
|
|
40
|
+
this.app.use(express.json({
|
|
41
|
+
type: (req) => {
|
|
42
|
+
const ct = req.headers['content-type'] || '';
|
|
43
|
+
return ct.includes('application/x-amz-json-1.1') || ct.includes('application/json');
|
|
44
|
+
}
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
// Request logging
|
|
48
|
+
if (logger.currentLogLevel === 'verboso') {
|
|
49
|
+
this.app.use((req, res, next) => {
|
|
50
|
+
const start = Date.now();
|
|
51
|
+
res.on('finish', () => {
|
|
52
|
+
const duration = Date.now() - start;
|
|
53
|
+
const target = req.headers['x-amz-target'] || '';
|
|
54
|
+
const action = req.body?.Action || '';
|
|
55
|
+
logger.verboso(`Aurora: ${target || action || req.method} - ${duration}ms`);
|
|
56
|
+
});
|
|
57
|
+
next();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async initialize() {
|
|
63
|
+
this.setupRoutes();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setupRoutes() {
|
|
67
|
+
// Main endpoint - route based on headers/body
|
|
68
|
+
this.app.post('/', async (req, res) => {
|
|
69
|
+
const target = req.headers['x-amz-target'] || '';
|
|
70
|
+
const body = req.body || {};
|
|
71
|
+
const action = body.Action || req.query?.Action || '';
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
let result;
|
|
75
|
+
|
|
76
|
+
if (target.startsWith('RDSDataService')) {
|
|
77
|
+
// Data API routing
|
|
78
|
+
if (!this.dataApiHandler) {
|
|
79
|
+
return res.status(503).json({
|
|
80
|
+
__type: 'ServiceUnavailableException',
|
|
81
|
+
message: 'Data API handler not initialized'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
result = await this.dataApiHandler.handleRequest(target, body);
|
|
85
|
+
} else if (action) {
|
|
86
|
+
// RDS Management API routing
|
|
87
|
+
if (!this.rdsApiHandler) {
|
|
88
|
+
return res.status(503).json({
|
|
89
|
+
__type: 'ServiceUnavailableException',
|
|
90
|
+
message: 'RDS API handler not initialized'
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
result = await this.rdsApiHandler.handleRequest(action, body);
|
|
94
|
+
} else {
|
|
95
|
+
// Unrecognized request
|
|
96
|
+
return res.status(400).json({
|
|
97
|
+
__type: 'BadRequestException',
|
|
98
|
+
message: 'Unrecognized request. Expected x-amz-target header starting with "RDSDataService" or an Action parameter.'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
res.json(result || {});
|
|
103
|
+
} catch (error) {
|
|
104
|
+
logger.error(`Aurora Error: ${error.message}`);
|
|
105
|
+
|
|
106
|
+
// If error already has an AWS-style code, use it directly
|
|
107
|
+
if (error.code && error.statusCode) {
|
|
108
|
+
return res.status(error.statusCode).json({
|
|
109
|
+
__type: error.code,
|
|
110
|
+
message: error.message
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Wrap raw SQLite/better-sqlite3 errors as BadRequestException
|
|
115
|
+
// These come from db.prepare(), stmt.run(), stmt.all(), etc.
|
|
116
|
+
if (error.message && (
|
|
117
|
+
error.message.includes('SQLITE_') ||
|
|
118
|
+
error.message.includes('no such table') ||
|
|
119
|
+
error.message.includes('no such column') ||
|
|
120
|
+
error.message.includes('near "') ||
|
|
121
|
+
error.message.includes('syntax error') ||
|
|
122
|
+
error.constructor?.name === 'SqliteError'
|
|
123
|
+
)) {
|
|
124
|
+
return res.status(400).json({
|
|
125
|
+
__type: 'BadRequestException',
|
|
126
|
+
message: error.message
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Default: treat unstructured errors as BadRequestException (matching AWS behavior)
|
|
131
|
+
const statusCode = error.statusCode || 400;
|
|
132
|
+
res.status(statusCode).json({
|
|
133
|
+
__type: error.code || 'BadRequestException',
|
|
134
|
+
message: error.message
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Admin endpoints
|
|
140
|
+
this.setupAdminRoutes();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
setupAdminRoutes() {
|
|
144
|
+
this.app.get('/__admin/health', (req, res) => {
|
|
145
|
+
res.json({
|
|
146
|
+
status: 'healthy',
|
|
147
|
+
service: 'aurora',
|
|
148
|
+
port: this.port,
|
|
149
|
+
running: !!this.server
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
this.app.get('/__admin/clusters', (req, res) => {
|
|
154
|
+
try {
|
|
155
|
+
const clusters = this.clusterManager ? this.clusterManager.listClusters() : [];
|
|
156
|
+
res.json({ clusters });
|
|
157
|
+
} catch (error) {
|
|
158
|
+
res.status(500).json({ error: error.message });
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
this.app.get('/__admin/databases', (req, res) => {
|
|
163
|
+
try {
|
|
164
|
+
const databases = this.dbEngine ? this.dbEngine.listDatabases() : [];
|
|
165
|
+
res.json({ databases });
|
|
166
|
+
} catch (error) {
|
|
167
|
+
res.status(500).json({ error: error.message });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
this.app.post('/__admin/reset', async (req, res) => {
|
|
172
|
+
try {
|
|
173
|
+
if (this.clusterManager) await this.clusterManager.reset();
|
|
174
|
+
if (this.dbEngine) await this.dbEngine.reset();
|
|
175
|
+
res.json({ message: 'Aurora data reset complete' });
|
|
176
|
+
} catch (error) {
|
|
177
|
+
res.status(500).json({ error: error.message });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
start() {
|
|
183
|
+
return new Promise((resolve) => {
|
|
184
|
+
this.server = this.app.listen(this.port, () => {
|
|
185
|
+
logger.info(`🌐 Aurora rodando em http://localhost:${this.port}`);
|
|
186
|
+
resolve();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
stop() {
|
|
192
|
+
return new Promise((resolve) => {
|
|
193
|
+
if (this.server) {
|
|
194
|
+
this.server.close(() => {
|
|
195
|
+
this.server = null;
|
|
196
|
+
resolve();
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
resolve();
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
getStatus() {
|
|
205
|
+
return {
|
|
206
|
+
running: !!this.server,
|
|
207
|
+
port: this.port,
|
|
208
|
+
endpoint: `http://localhost:${this.port}`
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
module.exports = AuroraServer;
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SQL Translator - Converte SQL MySQL/PostgreSQL para SQLite compatível.
|
|
5
|
+
*
|
|
6
|
+
* Responsabilidades:
|
|
7
|
+
* - Conversão de tipos de dados (VARCHAR→TEXT, INT→INTEGER, DOUBLE→REAL, etc.)
|
|
8
|
+
* - Tradução de funções (NOW()→datetime('now'), UUID()→hex expression)
|
|
9
|
+
* - Remoção de cláusulas não-suportadas (ENGINE, CHARSET, COLLATE, COMMENT)
|
|
10
|
+
* - Substituição AUTO_INCREMENT→AUTOINCREMENT
|
|
11
|
+
* - Normalização de whitespace e remoção de comentários
|
|
12
|
+
*
|
|
13
|
+
* IMPORTANTE: O método translate() NÃO muta a string SQL original.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
class SqlTranslator {
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} engineMode - 'aurora-mysql' | 'aurora-postgresql'
|
|
19
|
+
*/
|
|
20
|
+
constructor(engineMode) {
|
|
21
|
+
if (engineMode !== 'aurora-mysql' && engineMode !== 'aurora-postgresql') {
|
|
22
|
+
throw new Error(`Invalid engineMode: ${engineMode}. Must be 'aurora-mysql' or 'aurora-postgresql'`);
|
|
23
|
+
}
|
|
24
|
+
this.engineMode = engineMode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Translates SQL from MySQL/PostgreSQL dialect to SQLite-compatible SQL.
|
|
29
|
+
* Does NOT mutate the original SQL string.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} sql - Original SQL statement
|
|
32
|
+
* @returns {string} SQLite-compatible SQL
|
|
33
|
+
*/
|
|
34
|
+
translate(sql) {
|
|
35
|
+
if (!sql || typeof sql !== 'string') {
|
|
36
|
+
return sql;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Work on a copy - never mutate the original
|
|
40
|
+
let result = sql.trim();
|
|
41
|
+
|
|
42
|
+
// Step 1: Normalize whitespace and remove comments
|
|
43
|
+
result = this._removeComments(result);
|
|
44
|
+
result = this._normalizeWhitespace(result);
|
|
45
|
+
|
|
46
|
+
// Step 2: Translate data types (only in DDL context)
|
|
47
|
+
result = this._translateDataTypes(result);
|
|
48
|
+
|
|
49
|
+
// Step 3: Translate AUTO_INCREMENT
|
|
50
|
+
if (this.engineMode === 'aurora-mysql') {
|
|
51
|
+
result = result.replace(/\bAUTO_INCREMENT\b/gi, 'AUTOINCREMENT');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Step 4: Remove engine-specific clauses
|
|
55
|
+
result = this._removeEngineClauses(result);
|
|
56
|
+
|
|
57
|
+
// Step 5: Translate functions (apply globally)
|
|
58
|
+
result = this._translateFunctions(result);
|
|
59
|
+
|
|
60
|
+
// Step 6: Translate ON CONFLICT (PostgreSQL only)
|
|
61
|
+
if (this.engineMode === 'aurora-postgresql') {
|
|
62
|
+
result = this._translateOnConflict(result);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Remove SQL comments (single-line -- and multi-line /* *\/)
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
_removeComments(sql) {
|
|
73
|
+
// Remove multi-line comments /* ... */
|
|
74
|
+
let result = sql.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
75
|
+
// Remove single-line comments -- ...
|
|
76
|
+
result = result.replace(/--[^\n]*/g, '');
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Normalize whitespace: collapse multiple spaces/tabs/newlines into single space
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
_normalizeWhitespace(sql) {
|
|
85
|
+
return sql.replace(/\s+/g, ' ').trim();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Translate MySQL/PostgreSQL data types to SQLite equivalents.
|
|
90
|
+
* Only applies within CREATE TABLE statements to avoid replacing data in DML.
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
_translateDataTypes(sql) {
|
|
94
|
+
if (this.engineMode === 'aurora-postgresql') {
|
|
95
|
+
return this._translatePostgresDataTypes(sql);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (this.engineMode !== 'aurora-mysql') {
|
|
99
|
+
return sql;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check if this is a CREATE TABLE statement
|
|
103
|
+
if (!/\bCREATE\s+TABLE\b/i.test(sql)) {
|
|
104
|
+
return sql;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let result = sql;
|
|
108
|
+
|
|
109
|
+
// VARCHAR(N), CHAR(N) → TEXT
|
|
110
|
+
result = result.replace(/\bVARCHAR\s*\(\s*\d+\s*\)/gi, 'TEXT');
|
|
111
|
+
result = result.replace(/\bCHAR\s*\(\s*\d+\s*\)/gi, 'TEXT');
|
|
112
|
+
|
|
113
|
+
// TINYTEXT, MEDIUMTEXT, LONGTEXT → TEXT
|
|
114
|
+
result = result.replace(/\bTINYTEXT\b/gi, 'TEXT');
|
|
115
|
+
result = result.replace(/\bMEDIUMTEXT\b/gi, 'TEXT');
|
|
116
|
+
result = result.replace(/\bLONGTEXT\b/gi, 'TEXT');
|
|
117
|
+
|
|
118
|
+
// TINYINT, SMALLINT, MEDIUMINT → INTEGER (before INT to avoid partial match)
|
|
119
|
+
result = result.replace(/\bTINYINT\b/gi, 'INTEGER');
|
|
120
|
+
result = result.replace(/\bSMALLINT\b/gi, 'INTEGER');
|
|
121
|
+
result = result.replace(/\bMEDIUMINT\b/gi, 'INTEGER');
|
|
122
|
+
|
|
123
|
+
// BIGINT → INTEGER (before INT to avoid partial match)
|
|
124
|
+
result = result.replace(/\bBIGINT\b/gi, 'INTEGER');
|
|
125
|
+
|
|
126
|
+
// INT → INTEGER (word boundary prevents matching TINYINT, BIGINT, etc.)
|
|
127
|
+
result = result.replace(/\bINT\b/gi, 'INTEGER');
|
|
128
|
+
|
|
129
|
+
// FLOAT → REAL
|
|
130
|
+
result = result.replace(/\bFLOAT\b/gi, 'REAL');
|
|
131
|
+
|
|
132
|
+
// DOUBLE → REAL
|
|
133
|
+
result = result.replace(/\bDOUBLE\b/gi, 'REAL');
|
|
134
|
+
|
|
135
|
+
// DECIMAL(N,M) → REAL
|
|
136
|
+
result = result.replace(/\bDECIMAL\s*\(\s*\d+\s*,\s*\d+\s*\)/gi, 'REAL');
|
|
137
|
+
|
|
138
|
+
// DATETIME → TEXT
|
|
139
|
+
result = result.replace(/\bDATETIME\b/gi, 'TEXT');
|
|
140
|
+
|
|
141
|
+
// TIMESTAMP → TEXT
|
|
142
|
+
result = result.replace(/\bTIMESTAMP\b/gi, 'TEXT');
|
|
143
|
+
|
|
144
|
+
// DATE → TEXT (word boundary avoids matching CURDATE, DATETIME, etc.)
|
|
145
|
+
result = result.replace(/\bDATE\b/gi, 'TEXT');
|
|
146
|
+
|
|
147
|
+
// TIME → TEXT (word boundary avoids matching DATETIME, TIMESTAMP, etc.)
|
|
148
|
+
result = result.replace(/\bTIME\b/gi, 'TEXT');
|
|
149
|
+
|
|
150
|
+
// JSON → TEXT
|
|
151
|
+
result = result.replace(/\bJSON\b/gi, 'TEXT');
|
|
152
|
+
|
|
153
|
+
// TINYBLOB, MEDIUMBLOB, LONGBLOB → BLOB (before BLOB)
|
|
154
|
+
result = result.replace(/\bTINYBLOB\b/gi, 'BLOB');
|
|
155
|
+
result = result.replace(/\bMEDIUMBLOB\b/gi, 'BLOB');
|
|
156
|
+
result = result.replace(/\bLONGBLOB\b/gi, 'BLOB');
|
|
157
|
+
|
|
158
|
+
// BLOB → BLOB (already correct, but normalize case)
|
|
159
|
+
result = result.replace(/\bBLOB\b/gi, 'BLOB');
|
|
160
|
+
|
|
161
|
+
// ENUM(...) → TEXT
|
|
162
|
+
result = result.replace(/\bENUM\s*\([^)]*\)/gi, 'TEXT');
|
|
163
|
+
|
|
164
|
+
// SET(...) → TEXT
|
|
165
|
+
result = result.replace(/\bSET\s*\([^)]*\)/gi, 'TEXT');
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Remove engine-specific clauses (ENGINE, CHARSET, COLLATE, CHARACTER SET, COMMENT)
|
|
172
|
+
* @private
|
|
173
|
+
*/
|
|
174
|
+
_removeEngineClauses(sql) {
|
|
175
|
+
let result = sql;
|
|
176
|
+
|
|
177
|
+
// ENGINE=InnoDB (or any engine name)
|
|
178
|
+
result = result.replace(/\s*ENGINE\s*=\s*\w+/gi, '');
|
|
179
|
+
|
|
180
|
+
// DEFAULT CHARSET=utf8mb4 (or any charset)
|
|
181
|
+
result = result.replace(/\s*DEFAULT\s+CHARSET\s*=\s*\w+/gi, '');
|
|
182
|
+
|
|
183
|
+
// COLLATE = utf8mb4_unicode_ci or COLLATE utf8mb4_unicode_ci
|
|
184
|
+
result = result.replace(/\s*COLLATE\s*=?\s*\w+/gi, '');
|
|
185
|
+
|
|
186
|
+
// CHARACTER SET utf8
|
|
187
|
+
result = result.replace(/\s*CHARACTER\s+SET\s+\w+/gi, '');
|
|
188
|
+
|
|
189
|
+
// COMMENT 'text'
|
|
190
|
+
result = result.replace(/\s*COMMENT\s+'[^']*'/gi, '');
|
|
191
|
+
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Translate MySQL/PostgreSQL functions to SQLite equivalents.
|
|
197
|
+
* Applied globally (not just in DDL).
|
|
198
|
+
* @private
|
|
199
|
+
*/
|
|
200
|
+
_translateFunctions(sql) {
|
|
201
|
+
if (this.engineMode === 'aurora-postgresql') {
|
|
202
|
+
return this._translatePostgresFunctions(sql);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (this.engineMode !== 'aurora-mysql') {
|
|
206
|
+
return sql;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let result = sql;
|
|
210
|
+
|
|
211
|
+
// NOW() → datetime('now')
|
|
212
|
+
result = result.replace(/\bNOW\s*\(\s*\)/gi, "datetime('now')");
|
|
213
|
+
|
|
214
|
+
// CURDATE() → date('now')
|
|
215
|
+
result = result.replace(/\bCURDATE\s*\(\s*\)/gi, "date('now')");
|
|
216
|
+
|
|
217
|
+
// CURTIME() → time('now')
|
|
218
|
+
result = result.replace(/\bCURTIME\s*\(\s*\)/gi, "time('now')");
|
|
219
|
+
|
|
220
|
+
// UUID() → lower(hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6)))
|
|
221
|
+
result = result.replace(
|
|
222
|
+
/\bUUID\s*\(\s*\)/gi,
|
|
223
|
+
"lower(hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6)))"
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Translate PostgreSQL data types to SQLite equivalents.
|
|
231
|
+
* Only applies within CREATE TABLE statements to avoid replacing data in DML.
|
|
232
|
+
* @private
|
|
233
|
+
*/
|
|
234
|
+
_translatePostgresDataTypes(sql) {
|
|
235
|
+
// Check if this is a CREATE TABLE statement
|
|
236
|
+
if (!/\bCREATE\s+TABLE\b/i.test(sql)) {
|
|
237
|
+
return sql;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
let result = sql;
|
|
241
|
+
|
|
242
|
+
// BIGSERIAL → INTEGER PRIMARY KEY AUTOINCREMENT (before SERIAL to avoid partial match)
|
|
243
|
+
result = result.replace(/\bBIGSERIAL\b/gi, 'INTEGER PRIMARY KEY AUTOINCREMENT');
|
|
244
|
+
|
|
245
|
+
// SERIAL → INTEGER PRIMARY KEY AUTOINCREMENT
|
|
246
|
+
result = result.replace(/\bSERIAL\b/gi, 'INTEGER PRIMARY KEY AUTOINCREMENT');
|
|
247
|
+
|
|
248
|
+
// JSONB → TEXT (before JSON to avoid partial match)
|
|
249
|
+
result = result.replace(/\bJSONB\b/gi, 'TEXT');
|
|
250
|
+
|
|
251
|
+
// JSON → TEXT
|
|
252
|
+
result = result.replace(/\bJSON\b/gi, 'TEXT');
|
|
253
|
+
|
|
254
|
+
// BYTEA → BLOB
|
|
255
|
+
result = result.replace(/\bBYTEA\b/gi, 'BLOB');
|
|
256
|
+
|
|
257
|
+
// TIMESTAMPTZ → TEXT (before TIMESTAMP to avoid partial match)
|
|
258
|
+
result = result.replace(/\bTIMESTAMPTZ\b/gi, 'TEXT');
|
|
259
|
+
|
|
260
|
+
// TIMESTAMP → TEXT
|
|
261
|
+
result = result.replace(/\bTIMESTAMP\b/gi, 'TEXT');
|
|
262
|
+
|
|
263
|
+
// VARCHAR(N) → TEXT
|
|
264
|
+
result = result.replace(/\bVARCHAR\s*\(\s*\d+\s*\)/gi, 'TEXT');
|
|
265
|
+
|
|
266
|
+
// CHAR(N) → TEXT
|
|
267
|
+
result = result.replace(/\bCHAR\s*\(\s*\d+\s*\)/gi, 'TEXT');
|
|
268
|
+
|
|
269
|
+
// BOOLEAN → INTEGER
|
|
270
|
+
result = result.replace(/\bBOOLEAN\b/gi, 'INTEGER');
|
|
271
|
+
|
|
272
|
+
// NUMERIC(N,M) → REAL
|
|
273
|
+
result = result.replace(/\bNUMERIC\s*\(\s*\d+\s*,\s*\d+\s*\)/gi, 'REAL');
|
|
274
|
+
|
|
275
|
+
// DOUBLE PRECISION → REAL (before FLOAT to avoid conflicts)
|
|
276
|
+
result = result.replace(/\bDOUBLE\s+PRECISION\b/gi, 'REAL');
|
|
277
|
+
|
|
278
|
+
// FLOAT → REAL
|
|
279
|
+
result = result.replace(/\bFLOAT\b/gi, 'REAL');
|
|
280
|
+
|
|
281
|
+
// TEXT[] → TEXT (arrays stored as JSON text)
|
|
282
|
+
result = result.replace(/\bTEXT\s*\[\s*\]/gi, 'TEXT');
|
|
283
|
+
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Translate PostgreSQL functions to SQLite equivalents.
|
|
289
|
+
* Applied globally (not just in DDL).
|
|
290
|
+
* @private
|
|
291
|
+
*/
|
|
292
|
+
_translatePostgresFunctions(sql) {
|
|
293
|
+
let result = sql;
|
|
294
|
+
|
|
295
|
+
// NOW() → datetime('now')
|
|
296
|
+
result = result.replace(/\bNOW\s*\(\s*\)/gi, "datetime('now')");
|
|
297
|
+
|
|
298
|
+
// CURRENT_TIMESTAMP → datetime('now') (without parentheses - it's a keyword in PostgreSQL)
|
|
299
|
+
result = result.replace(/\bCURRENT_TIMESTAMP\b/gi, "datetime('now')");
|
|
300
|
+
|
|
301
|
+
// gen_random_uuid() → lower(hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6)))
|
|
302
|
+
result = result.replace(
|
|
303
|
+
/\bgen_random_uuid\s*\(\s*\)/gi,
|
|
304
|
+
"lower(hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6)))"
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
// COALESCE is already SQLite-compatible, no translation needed
|
|
308
|
+
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Translate PostgreSQL ON CONFLICT clauses to SQLite INSERT OR REPLACE/IGNORE.
|
|
314
|
+
* This is a simplification for the local simulator.
|
|
315
|
+
* @private
|
|
316
|
+
*/
|
|
317
|
+
_translateOnConflict(sql) {
|
|
318
|
+
// ON CONFLICT (...) DO NOTHING → INSERT OR IGNORE INTO ...
|
|
319
|
+
let result = sql.replace(
|
|
320
|
+
/\bINSERT\s+INTO\b(.*?)\bON\s+CONFLICT\s*\([^)]*\)\s*DO\s+NOTHING\b/gi,
|
|
321
|
+
'INSERT OR IGNORE INTO$1'
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// ON CONFLICT (...) DO UPDATE SET ... → INSERT OR REPLACE INTO ...
|
|
325
|
+
result = result.replace(
|
|
326
|
+
/\bINSERT\s+INTO\b(.*?)\bON\s+CONFLICT\s*\([^)]*\)\s*DO\s+UPDATE\s+SET\b[^;]*/gi,
|
|
327
|
+
'INSERT OR REPLACE INTO$1'
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
module.exports = SqlTranslator;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @fileoverview CloudFormation Service
|
|
5
|
-
* Porta padrão:
|
|
5
|
+
* Porta padrão: 4582
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const http = require('http');
|
|
@@ -16,7 +16,7 @@ class CloudFormationService {
|
|
|
16
16
|
this.config = config;
|
|
17
17
|
this.logger = require('../../utils/logger');
|
|
18
18
|
this.name = 'cloudformation';
|
|
19
|
-
this.port = config?.ports?.cloudformation || config?.services?.cloudformation?.port ||
|
|
19
|
+
this.port = config?.ports?.cloudformation || config?.services?.cloudformation?.port || 4582;
|
|
20
20
|
this.store = null;
|
|
21
21
|
this.simulator = null;
|
|
22
22
|
this._server = null;
|