synqdb-agent 1.0.3 → 1.0.5
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 +39 -40
- package/index.js +226 -175
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# synqdb-agent
|
|
2
2
|
|
|
3
|
-
Local database relay agent for [SynqDB](https://synqdb.
|
|
3
|
+
Local database relay agent for [SynqDB](https://synqdb.live) — connects your local databases to the SynqDB cloud dashboard without any firewall changes or port forwarding.
|
|
4
4
|
|
|
5
5
|
## How it works
|
|
6
6
|
|
|
@@ -24,78 +24,72 @@ npm install -g synqdb-agent
|
|
|
24
24
|
Or run without installing:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
npx synqdb-agent
|
|
27
|
+
npx synqdb-agent login
|
|
28
|
+
npx synqdb-agent
|
|
28
29
|
```
|
|
29
30
|
|
|
30
|
-
##
|
|
31
|
+
## Authentication
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
2. Click **Add Connection**
|
|
34
|
-
3. Toggle **Local Database**
|
|
35
|
-
4. Fill in your local DB credentials and click **Generate Agent Key**
|
|
36
|
-
5. Copy the key — it is only shown once
|
|
37
|
-
|
|
38
|
-
## Usage
|
|
39
|
-
|
|
40
|
-
### Recommended — save once, run forever
|
|
41
|
-
|
|
42
|
-
On first use, save your key:
|
|
33
|
+
Authentication is browser-based — no keys to copy or paste.
|
|
43
34
|
|
|
44
35
|
```bash
|
|
45
|
-
synqdb-agent
|
|
36
|
+
synqdb-agent login
|
|
46
37
|
```
|
|
47
38
|
|
|
48
|
-
|
|
39
|
+
This opens your browser to the SynqDB app. Log in (if you aren't already) and click **Authorize**. The CLI detects approval automatically and saves the credential to `~/.synqdb-agent`.
|
|
40
|
+
|
|
41
|
+
After that, just run:
|
|
49
42
|
|
|
50
43
|
```bash
|
|
51
44
|
synqdb-agent
|
|
52
45
|
```
|
|
53
46
|
|
|
54
|
-
|
|
47
|
+
## Usage
|
|
55
48
|
|
|
56
|
-
###
|
|
49
|
+
### Step 1 — Log in
|
|
57
50
|
|
|
58
51
|
```bash
|
|
59
|
-
synqdb-agent
|
|
52
|
+
synqdb-agent login
|
|
60
53
|
```
|
|
61
54
|
|
|
62
|
-
|
|
55
|
+
Opens your browser → click **Authorize** → done. Credential is saved automatically.
|
|
63
56
|
|
|
64
|
-
###
|
|
57
|
+
### Step 2 — Start the agent
|
|
65
58
|
|
|
66
59
|
```bash
|
|
67
|
-
|
|
60
|
+
synqdb-agent
|
|
68
61
|
```
|
|
69
62
|
|
|
70
|
-
|
|
63
|
+
The agent connects and stays running. Queries from your dashboard are routed through it in real time.
|
|
64
|
+
|
|
65
|
+
### Environment variable override
|
|
66
|
+
|
|
67
|
+
If you need to supply the key directly (e.g. in a CI/CD pipeline or Docker container), set:
|
|
71
68
|
|
|
72
69
|
```env
|
|
73
|
-
SYNQDB_AGENT_KEY
|
|
74
|
-
SYNQDB_SERVER_URL=https://api.synqdb.com
|
|
70
|
+
SYNQDB_AGENT_KEY=<your-agent-key>
|
|
75
71
|
```
|
|
76
72
|
|
|
77
|
-
|
|
73
|
+
The key resolution order is:
|
|
74
|
+
1. `SYNQDB_AGENT_KEY` environment variable
|
|
75
|
+
2. Saved credential at `~/.synqdb-agent` (written by `synqdb-agent login`)
|
|
78
76
|
|
|
79
|
-
|
|
77
|
+
### Custom server URL
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
### Environment variables
|
|
79
|
+
```env
|
|
80
|
+
SYNQDB_SERVER_URL=https://api.synqdb.live
|
|
81
|
+
SYNQDB_FRONTEND_URL=https://synqdb.live
|
|
82
|
+
```
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|---|---|---|
|
|
89
|
-
| `SYNQDB_AGENT_KEY` | Your agent key | — |
|
|
90
|
-
| `SYNQDB_SERVER_URL` | SynqDB API URL | `https://api.synqdb.com` |
|
|
84
|
+
Or place them in a `.env` file in the directory where you run the agent.
|
|
91
85
|
|
|
92
86
|
## Running persistently
|
|
93
87
|
|
|
94
|
-
To keep the agent running
|
|
88
|
+
To keep the agent running across terminal sessions and machine restarts, use [PM2](https://pm2.keymetrics.io):
|
|
95
89
|
|
|
96
90
|
```bash
|
|
97
91
|
npm install -g pm2
|
|
98
|
-
pm2 start synqdb-agent --name synqdb-agent
|
|
92
|
+
pm2 start synqdb-agent --name synqdb-agent
|
|
99
93
|
pm2 save
|
|
100
94
|
pm2 startup
|
|
101
95
|
```
|
|
@@ -109,6 +103,10 @@ pm2 restart synqdb-agent
|
|
|
109
103
|
pm2 stop synqdb-agent
|
|
110
104
|
```
|
|
111
105
|
|
|
106
|
+
## Revoking access
|
|
107
|
+
|
|
108
|
+
If you need to invalidate the current credential (e.g. the machine was compromised), go to **Dashboard → Project Settings → Local Agent → Rotate Key**. Any running agent will be disconnected. Run `synqdb-agent login` to re-authenticate.
|
|
109
|
+
|
|
112
110
|
## Supported databases
|
|
113
111
|
|
|
114
112
|
| Database | Driver |
|
|
@@ -119,10 +117,11 @@ pm2 stop synqdb-agent
|
|
|
119
117
|
|
|
120
118
|
## Security
|
|
121
119
|
|
|
122
|
-
-
|
|
120
|
+
- Authentication uses short-lived browser tokens (5-minute TTL) — no long-lived secrets are ever transmitted in a URL or terminal output
|
|
121
|
+
- The saved credential in `~/.synqdb-agent` is readable only by your user account (mode `0600`)
|
|
123
122
|
- The agent connects **outbound only** — no inbound ports are opened on your machine
|
|
124
123
|
- All queries are constructed server-side; the agent never builds SQL from user input
|
|
125
|
-
-
|
|
124
|
+
- Database credentials stay on your machine and are never sent to the SynqDB API
|
|
126
125
|
|
|
127
126
|
## License
|
|
128
127
|
|
package/index.js
CHANGED
|
@@ -26,212 +26,263 @@ function saveConfig(data) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
// ─── Resolve
|
|
29
|
+
// ─── Resolve serverUrl ────────────────────────────────────────────────────────
|
|
30
30
|
|
|
31
31
|
const saved = loadConfig();
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
const serverUrl =
|
|
34
|
+
process.env.SYNQDB_SERVER_URL ||
|
|
35
|
+
(saved.serverUrl?.startsWith('http') ? saved.serverUrl : null) ||
|
|
36
|
+
'https://api.synqdb.live';
|
|
37
|
+
|
|
38
|
+
const frontendUrl =
|
|
39
|
+
process.env.SYNQDB_FRONTEND_URL ||
|
|
40
|
+
(saved.frontendUrl?.startsWith('http') ? saved.frontendUrl : null) ||
|
|
41
|
+
'https://synqdb.live';
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
// ─── login command ────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
if (process.argv[2] === 'login') {
|
|
46
|
+
runLogin().catch((err) => {
|
|
47
|
+
console.error('Login failed:', err.message);
|
|
42
48
|
process.exit(1);
|
|
43
|
-
}
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
runAgent();
|
|
52
|
+
}
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
async function runLogin() {
|
|
55
|
+
// 1. Create a short-lived login token from the server
|
|
56
|
+
const initRes = await fetch(`${serverUrl}/v1/auth/cli-login/init`, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: { 'Content-Type': 'application/json' },
|
|
48
59
|
});
|
|
60
|
+
if (!initRes.ok) {
|
|
61
|
+
throw new Error(`Server error: ${initRes.status}`);
|
|
62
|
+
}
|
|
63
|
+
const { loginToken } = await initRes.json();
|
|
49
64
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
65
|
+
// 2. Open the authorize page in the user's browser
|
|
66
|
+
const authorizeUrl = `${frontendUrl}/agent/authorize?token=${loginToken}`;
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log(' Opening browser for authentication...');
|
|
69
|
+
console.log(` If the browser does not open, visit:\n ${authorizeUrl}`);
|
|
53
70
|
console.log('');
|
|
54
|
-
console.log('Run `synqdb-agent` with no arguments to start.');
|
|
55
|
-
process.exit(0);
|
|
56
|
-
}
|
|
57
71
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
try {
|
|
73
|
+
const open = require('open');
|
|
74
|
+
await open(authorizeUrl);
|
|
75
|
+
} catch {
|
|
76
|
+
// open might not be available in all environments — URL is printed above
|
|
77
|
+
}
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
'https://api.synqdb.com';
|
|
68
|
-
|
|
69
|
-
if (!agentKey) {
|
|
70
|
-
console.error('');
|
|
71
|
-
console.error(' No agent key found. Options:');
|
|
72
|
-
console.error('');
|
|
73
|
-
console.error(' 1. Save key once (recommended):');
|
|
74
|
-
console.error(' synqdb-agent --save <agentKey>');
|
|
75
|
-
console.error(' synqdb-agent');
|
|
76
|
-
console.error('');
|
|
77
|
-
console.error(' 2. Pass key each time:');
|
|
78
|
-
console.error(' synqdb-agent <agentKey>');
|
|
79
|
-
console.error('');
|
|
80
|
-
console.error(' 3. Set environment variable:');
|
|
81
|
-
console.error(' SYNQDB_AGENT_KEY=<agentKey> synqdb-agent');
|
|
82
|
-
console.error('');
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
79
|
+
// 3. Poll the server until the user approves (or token expires ~5 min)
|
|
80
|
+
const POLL_INTERVAL_MS = 2000;
|
|
81
|
+
const POLL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
82
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
if (process.argv[2] && !saved.agentKey) {
|
|
88
|
-
console.log(`Tip: run \`synqdb-agent --save ${process.argv[2]}\` to avoid typing the key next time.`);
|
|
89
|
-
}
|
|
84
|
+
process.stdout.write(' Waiting for approval');
|
|
90
85
|
|
|
91
|
-
|
|
86
|
+
while (Date.now() < deadline) {
|
|
87
|
+
await sleep(POLL_INTERVAL_MS);
|
|
88
|
+
process.stdout.write('.');
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
let pollRes;
|
|
91
|
+
try {
|
|
92
|
+
pollRes = await fetch(`${serverUrl}/v1/auth/cli-login/poll/${loginToken}`);
|
|
93
|
+
} catch {
|
|
94
|
+
// transient network error — keep retrying
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
95
97
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
98
|
+
if (!pollRes.ok) {
|
|
99
|
+
process.stdout.write('\n');
|
|
100
|
+
throw new Error(`Token expired or invalid (server returned ${pollRes.status})`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const body = await pollRes.json();
|
|
99
104
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
port: payload.port,
|
|
110
|
-
user: payload.username,
|
|
111
|
-
password: payload.password || undefined,
|
|
112
|
-
database: payload.database,
|
|
113
|
-
waitForConnections: true,
|
|
114
|
-
connectionLimit: 5,
|
|
115
|
-
multipleStatements: true,
|
|
116
|
-
});
|
|
117
|
-
connectionCache.set(key, pool);
|
|
105
|
+
if (body.status === 'authorized') {
|
|
106
|
+
process.stdout.write('\n');
|
|
107
|
+
saveConfig({ agentKey: body.agentKey, serverUrl });
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log(' ✓ Authenticated! Agent key saved to', CONFIG_PATH);
|
|
110
|
+
console.log(' Run `synqdb-agent` with no arguments to start the agent.');
|
|
111
|
+
console.log('');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
118
114
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
|
|
116
|
+
process.stdout.write('\n');
|
|
117
|
+
throw new Error('Login timed out. Please run `synqdb-agent login` again.');
|
|
122
118
|
}
|
|
123
119
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
async function runPostgres(payload) {
|
|
127
|
-
const { Pool } = require('pg');
|
|
128
|
-
const key = cacheKey(payload);
|
|
129
|
-
let pool = connectionCache.get(key);
|
|
130
|
-
if (!pool) {
|
|
131
|
-
pool = new Pool({
|
|
132
|
-
host: payload.host,
|
|
133
|
-
port: payload.port,
|
|
134
|
-
user: payload.username,
|
|
135
|
-
password: payload.password || undefined,
|
|
136
|
-
database: payload.database,
|
|
137
|
-
max: 5,
|
|
138
|
-
idleTimeoutMillis: 30000,
|
|
139
|
-
connectionTimeoutMillis: 10000,
|
|
140
|
-
});
|
|
141
|
-
connectionCache.set(key, pool);
|
|
142
|
-
}
|
|
143
|
-
const res = await pool.query(payload.sql, payload.params || []);
|
|
144
|
-
return { rows: res.rows, rowCount: res.rowCount ?? res.rows.length };
|
|
120
|
+
function sleep(ms) {
|
|
121
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
145
122
|
}
|
|
146
123
|
|
|
147
|
-
// ───
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
});
|
|
162
|
-
await pool.connect();
|
|
163
|
-
connectionCache.set(key, pool);
|
|
124
|
+
// ─── Agent runner ─────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
function runAgent() {
|
|
127
|
+
const agentKey = process.env.SYNQDB_AGENT_KEY || saved.agentKey;
|
|
128
|
+
|
|
129
|
+
if (!agentKey) {
|
|
130
|
+
console.error('');
|
|
131
|
+
console.error(' No agent key found. Run:');
|
|
132
|
+
console.error('');
|
|
133
|
+
console.error(' synqdb-agent login');
|
|
134
|
+
console.error('');
|
|
135
|
+
console.error(' This opens your browser to authenticate — no key to copy.');
|
|
136
|
+
console.error('');
|
|
137
|
+
process.exit(1);
|
|
164
138
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
139
|
+
|
|
140
|
+
console.log(`Connecting to SynqDB at ${serverUrl} ...`);
|
|
141
|
+
|
|
142
|
+
// Cache connections by a composite key so we don't open a new connection per query
|
|
143
|
+
const connectionCache = new Map();
|
|
144
|
+
|
|
145
|
+
function cacheKey(payload) {
|
|
146
|
+
return `${payload.type}:${payload.host}:${payload.port}:${payload.database}:${payload.username}`;
|
|
170
147
|
}
|
|
171
|
-
const result = await request.query(payload.sql);
|
|
172
|
-
const rows = result.recordset || [];
|
|
173
|
-
return { rows, rowCount: result.rowsAffected?.[0] ?? rows.length };
|
|
174
|
-
}
|
|
175
148
|
|
|
176
|
-
// ───
|
|
149
|
+
// ─── MySQL ──────────────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
async function runMySQL(payload) {
|
|
152
|
+
const mysql = require('mysql2/promise');
|
|
153
|
+
const key = cacheKey(payload);
|
|
154
|
+
let pool = connectionCache.get(key);
|
|
155
|
+
if (!pool) {
|
|
156
|
+
pool = mysql.createPool({
|
|
157
|
+
host: payload.host,
|
|
158
|
+
port: payload.port,
|
|
159
|
+
user: payload.username,
|
|
160
|
+
password: payload.password || undefined,
|
|
161
|
+
database: payload.database,
|
|
162
|
+
waitForConnections: true,
|
|
163
|
+
connectionLimit: 5,
|
|
164
|
+
multipleStatements: true,
|
|
165
|
+
});
|
|
166
|
+
connectionCache.set(key, pool);
|
|
167
|
+
}
|
|
168
|
+
const [rows] = await pool.query(payload.sql, payload.params || []);
|
|
169
|
+
const data = Array.isArray(rows) ? rows : [rows];
|
|
170
|
+
return { rows: data, rowCount: data.length };
|
|
171
|
+
}
|
|
177
172
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
173
|
+
// ─── PostgreSQL ─────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
async function runPostgres(payload) {
|
|
176
|
+
const { Pool } = require('pg');
|
|
177
|
+
const key = cacheKey(payload);
|
|
178
|
+
let pool = connectionCache.get(key);
|
|
179
|
+
if (!pool) {
|
|
180
|
+
pool = new Pool({
|
|
181
|
+
host: payload.host,
|
|
182
|
+
port: payload.port,
|
|
183
|
+
user: payload.username,
|
|
184
|
+
password: payload.password || undefined,
|
|
185
|
+
database: payload.database,
|
|
186
|
+
max: 5,
|
|
187
|
+
idleTimeoutMillis: 30000,
|
|
188
|
+
connectionTimeoutMillis: 10000,
|
|
189
|
+
});
|
|
190
|
+
connectionCache.set(key, pool);
|
|
191
|
+
}
|
|
192
|
+
const res = await pool.query(payload.sql, payload.params || []);
|
|
193
|
+
return { rows: res.rows, rowCount: res.rowCount ?? res.rows.length };
|
|
184
194
|
}
|
|
185
|
-
}
|
|
186
195
|
|
|
187
|
-
// ───
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
196
|
+
// ─── MSSQL ──────────────────────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
async function runMSSQL(payload) {
|
|
199
|
+
const mssql = require('mssql');
|
|
200
|
+
const key = cacheKey(payload);
|
|
201
|
+
let pool = connectionCache.get(key);
|
|
202
|
+
if (!pool || !pool.connected) {
|
|
203
|
+
pool = new mssql.ConnectionPool({
|
|
204
|
+
server: payload.host,
|
|
205
|
+
port: payload.port || 1433,
|
|
206
|
+
user: payload.username,
|
|
207
|
+
password: payload.password || undefined,
|
|
208
|
+
database: payload.database,
|
|
209
|
+
options: { encrypt: true, trustServerCertificate: true },
|
|
210
|
+
});
|
|
211
|
+
await pool.connect();
|
|
212
|
+
connectionCache.set(key, pool);
|
|
213
|
+
}
|
|
214
|
+
const request = pool.request();
|
|
215
|
+
if (payload.namedParams) {
|
|
216
|
+
for (const [name, value] of Object.entries(payload.namedParams)) {
|
|
217
|
+
request.input(name, value);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const result = await request.query(payload.sql);
|
|
221
|
+
const rows = result.recordset || [];
|
|
222
|
+
return { rows, rowCount: result.rowsAffected?.[0] ?? rows.length };
|
|
206
223
|
}
|
|
207
|
-
});
|
|
208
224
|
|
|
209
|
-
|
|
210
|
-
console.error(`Authentication failed: ${message}`);
|
|
211
|
-
process.exit(1);
|
|
212
|
-
});
|
|
225
|
+
// ─── Dispatch ────────────────────────────────────────────────────────────────
|
|
213
226
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
socket.emit('error', { requestId, message: err.message });
|
|
227
|
+
async function executeQuery(payload) {
|
|
228
|
+
switch (payload.type) {
|
|
229
|
+
case 'mysql': return runMySQL(payload);
|
|
230
|
+
case 'postgres': return runPostgres(payload);
|
|
231
|
+
case 'mssql': return runMSSQL(payload);
|
|
232
|
+
default: throw new Error(`Unsupported database type: ${payload.type}`);
|
|
233
|
+
}
|
|
222
234
|
}
|
|
223
|
-
});
|
|
224
235
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
236
|
+
// ─── Socket.IO ──────────────────────────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
const socket = io(`${serverUrl}/agent`, {
|
|
239
|
+
reconnectionDelay: 2000,
|
|
240
|
+
reconnectionDelayMax: 10000,
|
|
241
|
+
transports: ['websocket'],
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
socket.on('connect', () => {
|
|
245
|
+
console.log('Connected. Authenticating ...');
|
|
246
|
+
socket.emit('register', { agentKey });
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
socket.on('registered', ({ clusterId }) => {
|
|
250
|
+
console.log(`Authenticated. Serving cluster: ${clusterId}`);
|
|
251
|
+
});
|
|
228
252
|
|
|
229
|
-
socket.on('
|
|
230
|
-
|
|
231
|
-
});
|
|
253
|
+
socket.on('auth_error', ({ message }) => {
|
|
254
|
+
if (message && message.includes('rotated')) {
|
|
255
|
+
console.error(`\n ${message}`);
|
|
256
|
+
console.error(' Run `synqdb-agent login` to re-authenticate.\n');
|
|
257
|
+
} else {
|
|
258
|
+
console.error(`Authentication failed: ${message}`);
|
|
259
|
+
console.error('Run `synqdb-agent login` to re-authenticate.');
|
|
260
|
+
}
|
|
261
|
+
process.exit(1);
|
|
262
|
+
});
|
|
232
263
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
});
|
|
264
|
+
socket.on('query', async (payload) => {
|
|
265
|
+
const { requestId } = payload;
|
|
266
|
+
try {
|
|
267
|
+
const { rows, rowCount } = await executeQuery(payload);
|
|
268
|
+
socket.emit('result', { requestId, rows, rowCount });
|
|
269
|
+
} catch (err) {
|
|
270
|
+
console.error(`Query error [${requestId}]:`, err.message);
|
|
271
|
+
socket.emit('error', { requestId, message: err.message });
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
socket.on('disconnect', (reason) => {
|
|
276
|
+
console.log(`Disconnected: ${reason}. Reconnecting ...`);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
socket.on('connect_error', (err) => {
|
|
280
|
+
console.error('Connection error:', err.message);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
process.on('SIGINT', () => {
|
|
284
|
+
console.log('\nShutting down agent.');
|
|
285
|
+
socket.disconnect();
|
|
286
|
+
process.exit(0);
|
|
287
|
+
});
|
|
288
|
+
}
|