@duckfly/proxy 1.0.3 → 1.0.4
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 +1 -1
- package/bin/cli.js +44 -22
- package/package.json +2 -2
- package/src/api-client.js +1 -1
- package/src/proxy-server.js +8 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ After running, the CLI will ask for:
|
|
|
43
43
|
|
|
44
44
|
| Prompt | Description | Default |
|
|
45
45
|
|---|---|---|
|
|
46
|
-
| **Token** | Your Duckfly application token |
|
|
46
|
+
| **Token** | Your Duckfly application token | N/D |
|
|
47
47
|
| **Proxy Port** | Port where the proxy listens | `8080` |
|
|
48
48
|
| **Target URL** | Your backend API address | `http://localhost:3000` |
|
|
49
49
|
|
package/bin/cli.js
CHANGED
|
@@ -51,9 +51,7 @@ let config = {
|
|
|
51
51
|
appUrl: null,
|
|
52
52
|
domainPackageId: null,
|
|
53
53
|
proxyPort: 8080,
|
|
54
|
-
targetUrl:
|
|
55
|
-
apiUrl: DUCKFLY_API_URL,
|
|
56
|
-
proxyUrl: DUCKFLY_PROXY_URL
|
|
54
|
+
targetUrl: null,
|
|
57
55
|
};
|
|
58
56
|
|
|
59
57
|
const CONFIG_FILE = path.join(process.cwd(), '.duckfly-proxy.json');
|
|
@@ -62,7 +60,8 @@ function loadConfig() {
|
|
|
62
60
|
try {
|
|
63
61
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
64
62
|
const saved = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
65
|
-
|
|
63
|
+
const { apiUrl, proxyUrl, ...rest } = saved;
|
|
64
|
+
config = { ...config, ...rest };
|
|
66
65
|
return true;
|
|
67
66
|
}
|
|
68
67
|
} catch (error) {
|
|
@@ -73,7 +72,8 @@ function loadConfig() {
|
|
|
73
72
|
|
|
74
73
|
function saveConfig() {
|
|
75
74
|
try {
|
|
76
|
-
|
|
75
|
+
const { token, appName, appUrl, domainPackageId, proxyPort, targetUrl, hostAlias } = config;
|
|
76
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify({ token, appName, appUrl, domainPackageId, proxyPort, targetUrl, hostAlias }, null, 2));
|
|
77
77
|
} catch (error) {
|
|
78
78
|
console.log(chalk.yellow('⚠️ Não foi possível salvar a configuração'));
|
|
79
79
|
}
|
|
@@ -85,7 +85,7 @@ async function validateToken(token) {
|
|
|
85
85
|
spinner.start();
|
|
86
86
|
|
|
87
87
|
try {
|
|
88
|
-
const apiClient = new ApiClient(
|
|
88
|
+
const apiClient = new ApiClient(DUCKFLY_API_URL, DUCKFLY_PROXY_URL, token);
|
|
89
89
|
const result = await apiClient.validateToken();
|
|
90
90
|
|
|
91
91
|
spinner.stop(true);
|
|
@@ -96,7 +96,6 @@ async function validateToken(token) {
|
|
|
96
96
|
console.log(chalk.gray('─'.repeat(60)));
|
|
97
97
|
console.log(chalk.yellow(' Nome: ') + chalk.white(result.appName));
|
|
98
98
|
console.log(chalk.yellow(' URL: ') + chalk.white(result.appUrl));
|
|
99
|
-
console.log(chalk.yellow(' Package ID: ') + chalk.white(result.domainPackageId));
|
|
100
99
|
console.log(chalk.gray('─'.repeat(60)) + '\n');
|
|
101
100
|
|
|
102
101
|
config.appName = result.appName;
|
|
@@ -143,10 +142,7 @@ async function askQuestions() {
|
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
144
|
|
|
146
|
-
|
|
147
|
-
config.proxyUrl = DUCKFLY_PROXY_URL;
|
|
148
|
-
|
|
149
|
-
const questions = [
|
|
145
|
+
const { proxyPort } = await inquirer.prompt([
|
|
150
146
|
{
|
|
151
147
|
type: 'input',
|
|
152
148
|
name: 'proxyPort',
|
|
@@ -159,12 +155,37 @@ async function askQuestions() {
|
|
|
159
155
|
}
|
|
160
156
|
return true;
|
|
161
157
|
}
|
|
162
|
-
}
|
|
158
|
+
}
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
config.proxyPort = proxyPort;
|
|
162
|
+
|
|
163
|
+
// Alias: se o domínio do proxy não bate com o da aplicação, ativa automaticamente
|
|
164
|
+
config.hostAlias = null;
|
|
165
|
+
if (config.appUrl) {
|
|
166
|
+
try {
|
|
167
|
+
const appHost = new URL(config.appUrl).host;
|
|
168
|
+
const proxyHost = `localhost:${config.proxyPort}`;
|
|
169
|
+
|
|
170
|
+
if (appHost && appHost !== proxyHost && appHost !== `127.0.0.1:${config.proxyPort}`) {
|
|
171
|
+
config.hostAlias = appHost;
|
|
172
|
+
console.log('');
|
|
173
|
+
console.log(chalk.blue(' O domínio da aplicação (') + chalk.bold.white(appHost) + chalk.blue(') difere do proxy local.'));
|
|
174
|
+
console.log(chalk.blue(' As requisições capturadas serão enviadas com o domínio ') + chalk.bold.white(appHost));
|
|
175
|
+
console.log(chalk.blue(' para que possam ser processadas corretamente pela aplicação.\n'));
|
|
176
|
+
}
|
|
177
|
+
} catch { }
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const isWildcard = config.appUrl && config.appUrl.includes('*');
|
|
181
|
+
const defaultTarget = isWildcard ? null : (config.appUrl || config.targetUrl);
|
|
182
|
+
|
|
183
|
+
const { targetUrl } = await inquirer.prompt([
|
|
163
184
|
{
|
|
164
185
|
type: 'input',
|
|
165
186
|
name: 'targetUrl',
|
|
166
187
|
message: chalk.yellow('🎯 URL de destino (para onde encaminhar as requisições):'),
|
|
167
|
-
default:
|
|
188
|
+
default: defaultTarget,
|
|
168
189
|
validate: (input) => {
|
|
169
190
|
try {
|
|
170
191
|
new URL(input);
|
|
@@ -174,13 +195,11 @@ async function askQuestions() {
|
|
|
174
195
|
}
|
|
175
196
|
}
|
|
176
197
|
}
|
|
177
|
-
];
|
|
178
|
-
|
|
179
|
-
const answers = await inquirer.prompt(questions);
|
|
198
|
+
]);
|
|
180
199
|
|
|
181
|
-
|
|
200
|
+
config.targetUrl = targetUrl;
|
|
182
201
|
|
|
183
|
-
return
|
|
202
|
+
return;
|
|
184
203
|
}
|
|
185
204
|
|
|
186
205
|
let proxyServer = null;
|
|
@@ -188,7 +207,7 @@ let proxyServer = null;
|
|
|
188
207
|
async function startProxy() {
|
|
189
208
|
console.log(chalk.yellow('\n🚀 Iniciando Duckfly Proxy...\n'));
|
|
190
209
|
|
|
191
|
-
proxyServer = new ProxyServer(config);
|
|
210
|
+
proxyServer = new ProxyServer({ ...config, apiUrl: DUCKFLY_API_URL, proxyUrl: DUCKFLY_PROXY_URL });
|
|
192
211
|
|
|
193
212
|
try {
|
|
194
213
|
await proxyServer.start();
|
|
@@ -200,8 +219,8 @@ async function startProxy() {
|
|
|
200
219
|
console.log(chalk.yellow(' Proxy rodando em: ') + chalk.white(`http://localhost:${config.proxyPort}`));
|
|
201
220
|
console.log(chalk.yellow(' Encaminhando para: ') + chalk.white(config.targetUrl));
|
|
202
221
|
console.log(chalk.gray('─'.repeat(60)));
|
|
203
|
-
console.log(chalk.
|
|
204
|
-
console.log(chalk.
|
|
222
|
+
console.log(chalk.blue('\n💡 Dica: Configure sua aplicação para apontar para ') + chalk.white(`http://localhost:${config.proxyPort}`));
|
|
223
|
+
console.log(chalk.blue(' Todas as requisições serão capturadas e documentadas automaticamente!\n'));
|
|
205
224
|
console.log(chalk.gray('Pressione Ctrl+C para parar o proxy\n'));
|
|
206
225
|
|
|
207
226
|
saveConfig();
|
|
@@ -242,8 +261,11 @@ async function main() {
|
|
|
242
261
|
if (!useExisting) {
|
|
243
262
|
config.token = null;
|
|
244
263
|
config.appName = null;
|
|
264
|
+
config.appUrl = null;
|
|
265
|
+
config.domainPackageId = null;
|
|
245
266
|
config.proxyPort = 8080;
|
|
246
|
-
config.targetUrl =
|
|
267
|
+
config.targetUrl = null;
|
|
268
|
+
try { fs.unlinkSync(CONFIG_FILE); } catch { }
|
|
247
269
|
}
|
|
248
270
|
}
|
|
249
271
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duckfly/proxy",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Duckfly Proxy observes real API usage to continuously enrich and generate API documentation and MCP servers on Duckfly.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -47,4 +47,4 @@
|
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public"
|
|
49
49
|
}
|
|
50
|
-
}
|
|
50
|
+
}
|
package/src/api-client.js
CHANGED
|
@@ -9,7 +9,7 @@ class ApiClient {
|
|
|
9
9
|
|
|
10
10
|
async validateToken() {
|
|
11
11
|
try {
|
|
12
|
-
const url = `${this.apiUrl}/internal/v1/core/token/${this.token}`;
|
|
12
|
+
const url = `${this.apiUrl}/internal/v1/core/token/${this.token}?source=plugin-proxy`;
|
|
13
13
|
|
|
14
14
|
const response = await axios.get(url, {
|
|
15
15
|
timeout: 30000,
|
package/src/proxy-server.js
CHANGED
|
@@ -9,6 +9,7 @@ const { sanitizeRequestData } = require('./sanitizer');
|
|
|
9
9
|
class ProxyServer {
|
|
10
10
|
constructor(config) {
|
|
11
11
|
this.config = config;
|
|
12
|
+
this.hostAlias = config.hostAlias || null;
|
|
12
13
|
this.proxy = httpProxy.createProxyServer({});
|
|
13
14
|
this.app = express();
|
|
14
15
|
this.server = null;
|
|
@@ -85,10 +86,16 @@ class ProxyServer {
|
|
|
85
86
|
|
|
86
87
|
captureRequest(req, res) {
|
|
87
88
|
try {
|
|
89
|
+
const headers = { ...req.headers };
|
|
90
|
+
|
|
91
|
+
if (this.hostAlias) {
|
|
92
|
+
headers.host = this.hostAlias;
|
|
93
|
+
}
|
|
94
|
+
|
|
88
95
|
req._captureData = {
|
|
89
96
|
method: req.method,
|
|
90
97
|
url: req.url,
|
|
91
|
-
headers
|
|
98
|
+
headers,
|
|
92
99
|
body: this.parseBody(req.rawBody, req.headers['content-type']),
|
|
93
100
|
protocol: req.socket.encrypted ? 'https' : 'http',
|
|
94
101
|
timestamp: new Date().toISOString(),
|