pglens 1.1.0 → 2.0.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/CHANGELOG.md +17 -0
- package/README.md +38 -145
- package/bin/pglens +143 -7
- package/client/app.js +654 -128
- package/client/index.html +133 -35
- package/client/styles.css +671 -37
- package/package.json +2 -2
- package/src/db/connection.js +163 -27
- package/src/routes/api.js +139 -9
- package/src/server.js +72 -30
- package/pglens-1.1.0.tgz +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.0] - 2026-01-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Server-Side Sorting**: Table sorting is now performed on the database server, ensuring correct ordering across all data pages.
|
|
13
|
+
- **Connection Parameters Mode**: New tabbed interface in connection dialog allowing separate input for Host, Port, User, Password, and Database.
|
|
14
|
+
- **Global Loading Overlay**: Blocks user interaction during data fetching to prevent race conditions.
|
|
15
|
+
- **Dynamic Port Selection**: Server automatically finds an available port if 54321 is busy.
|
|
16
|
+
- **`pglens url` Command**: CLI command to display the currently running server URL.
|
|
17
|
+
- **Database Query Timeout**: Enforced 30-second timeout on all database operations.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Connection dialog now defaults to "Parameters" input mode for better usability.
|
|
22
|
+
- Switched to offset-based pagination when a custom sort order is active.
|
|
23
|
+
- Improved connection error handling and validation.
|
|
24
|
+
|
|
8
25
|
## [1.1.0] - 2025-11-21
|
|
9
26
|
|
|
10
27
|
### Added
|
package/README.md
CHANGED
|
@@ -4,18 +4,23 @@ A simple PostgreSQL database viewer tool. Perfect to quickly view and explore yo
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
+
- 🔌 **Connection Manager**: Manage multiple database connections from a single UI
|
|
8
|
+
- 🚀 **Background Service**: Runs as a daemon process for persistent access
|
|
7
9
|
- 🗂️ **Table Browser**: View all tables in your database in a clean, searchable sidebar
|
|
8
10
|
- 📊 **Data Viewer**: Browse table rows with a modern, easy-to-read interface
|
|
11
|
+
- 📝 **Cell Content Viewer**: Double-click any cell to view full content in a popup
|
|
12
|
+
- 🎨 **JSON/JSONB Formatting**: Auto-formats JSON data with syntax highlighting
|
|
13
|
+
- 🕒 **Timezone Support**: View timestamps in local, UTC, or other timezones
|
|
14
|
+
- 📋 **Clipboard Support**: One-click copy for cell contents
|
|
9
15
|
- 🪟 **Multiple Tabs**: Open multiple tables simultaneously in separate tabs
|
|
10
|
-
- 🔄 **Sorting**: Click column headers to sort data
|
|
11
|
-
- 📄 **Pagination**: Navigate through large tables with Previous/Next buttons
|
|
16
|
+
- 🔄 **Server-Side Sorting**: Click column headers to sort data directly on the database server
|
|
17
|
+
- 📄 **Pagination**: Navigate through large tables with Previous/Next buttons
|
|
12
18
|
- 🔍 **Table Search**: Quickly find tables by name using the search bar
|
|
13
19
|
- 👁️ **Column Visibility**: Show or hide columns to focus on what matters
|
|
14
20
|
- 📏 **Column Resizing**: Resize columns by dragging the column borders
|
|
15
21
|
- 🎨 **Theme Support**: Choose between light, dark, or system theme
|
|
16
|
-
- 🔄 **Refresh Data**: Reload table data with a single click
|
|
17
22
|
- ⚡ **Optimized Performance**: Uses cursor-based pagination for efficient large table navigation
|
|
18
|
-
- 🔒 **SSL Support**: Configurable SSL modes
|
|
23
|
+
- 🔒 **SSL Support**: Configurable SSL modes (Disable, Require, Prefer, Verify CA/Full)
|
|
19
24
|
- 🚀 **Easy Setup**: Install globally and run with a single command
|
|
20
25
|
|
|
21
26
|
## Installation
|
|
@@ -32,173 +37,63 @@ npm install pglens
|
|
|
32
37
|
|
|
33
38
|
## Usage
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
### Start the Server
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
pglens --url postgresql://user:password@localhost:5432/dbname --port 54321
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Arguments
|
|
42
|
-
|
|
43
|
-
- `--url` (required): PostgreSQL connection string
|
|
44
|
-
- Format: `postgresql://user:password@host:port/database`
|
|
45
|
-
- Example: `postgresql://postgres:mypassword@localhost:5432/mydb`
|
|
46
|
-
- `--port` (optional): Port to run the web server on (default: 54321)
|
|
47
|
-
- `--sslmode` (optional): SSL mode for database connection (default: `prefer`)
|
|
48
|
-
- `disable`: Disable SSL encryption
|
|
49
|
-
- `require`: Require SSL, but don't verify certificate
|
|
50
|
-
- `prefer`: Prefer SSL, but allow non-SSL fallback (default)
|
|
51
|
-
- `verify-ca`: Require SSL and verify certificate authority
|
|
52
|
-
- `verify-full`: Require SSL and verify certificate and hostname
|
|
53
|
-
|
|
54
|
-
### SSL Mode Examples
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
# Use default SSL mode (prefer)
|
|
58
|
-
pglens --url postgresql://postgres:secret@localhost:5432/myapp
|
|
59
|
-
|
|
60
|
-
# Require SSL without certificate verification (for self-signed certs)
|
|
61
|
-
pglens --url postgresql://postgres:secret@localhost:5432/myapp --sslmode require
|
|
62
|
-
|
|
63
|
-
# Disable SSL (for local development)
|
|
64
|
-
pglens --url postgresql://postgres:secret@localhost:5432/myapp --sslmode disable
|
|
65
|
-
|
|
66
|
-
# Full certificate verification (for production)
|
|
67
|
-
pglens --url postgresql://postgres:secret@localhost:5432/myapp --sslmode verify-full
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Connection Troubleshooting
|
|
71
|
-
|
|
72
|
-
If you encounter connection errors, pglens will automatically analyze the error and suggest an appropriate SSL mode:
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
✗ Failed to connect to PostgreSQL database: self signed certificate
|
|
76
|
-
|
|
77
|
-
💡 SSL Mode Recommendation: Try using --sslmode require
|
|
78
|
-
Current SSL mode: verify-full
|
|
79
|
-
Suggested command: Add --sslmode require to your command
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Basic Example
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
pglens --url postgresql://postgres:secret@localhost:5432/myapp --port 54321
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Then open your browser to `http://localhost:54321` to view your database.
|
|
89
|
-
|
|
90
|
-
## Running with PM2 (Server Deployment)
|
|
91
|
-
|
|
92
|
-
For production use on a server, you can run pglens as a persistent process using [PM2](https://pm2.keymetrics.io/), a process manager for Node.js applications.
|
|
93
|
-
|
|
94
|
-
### Install PM2
|
|
42
|
+
Start pglens as a background service:
|
|
95
43
|
|
|
96
44
|
```bash
|
|
97
|
-
|
|
45
|
+
pglens start
|
|
98
46
|
```
|
|
99
47
|
|
|
100
|
-
|
|
48
|
+
This will start the server on `http://localhost:54321` (or the next available port if 54321 is busy).
|
|
49
|
+
The URL will be printed to the console. You can also check the running URL at any time with:
|
|
101
50
|
|
|
102
51
|
```bash
|
|
103
|
-
|
|
52
|
+
pglens url
|
|
104
53
|
```
|
|
105
54
|
|
|
106
|
-
|
|
55
|
+
### Connect to a Database
|
|
107
56
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
57
|
+
1. Open `http://localhost:54321`
|
|
58
|
+
2. Click the **+** icon in the sidebar
|
|
59
|
+
3. Enter your connection details using one of the tabs:
|
|
60
|
+
- **Parameters** (Default): Enter Host, Port, Database, User, and Password separately.
|
|
61
|
+
- **Connection URL**: Paste a standard PostgreSQL connection string (e.g., `postgresql://user:pass@localhost:5432/db`).
|
|
62
|
+
4. Select the **SSL Mode** appropriate for your server.
|
|
63
|
+
5. Click **Connect**.
|
|
111
64
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
**ecosystem.config.js:**
|
|
115
|
-
|
|
116
|
-
```javascript
|
|
117
|
-
module.exports = {
|
|
118
|
-
apps: [
|
|
119
|
-
{
|
|
120
|
-
name: "pglens",
|
|
121
|
-
script: "pglens",
|
|
122
|
-
args: "--url postgresql://user:password@localhost:5432/dbname --port 54321 --sslmode require",
|
|
123
|
-
instances: 1,
|
|
124
|
-
autorestart: true,
|
|
125
|
-
watch: false,
|
|
126
|
-
max_memory_restart: "300M",
|
|
127
|
-
env: {
|
|
128
|
-
NODE_ENV: "production",
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
],
|
|
132
|
-
};
|
|
133
|
-
```
|
|
65
|
+
### Stop the Server
|
|
134
66
|
|
|
135
|
-
|
|
67
|
+
To stop the background service:
|
|
136
68
|
|
|
137
69
|
```bash
|
|
138
|
-
|
|
70
|
+
pglens stop
|
|
139
71
|
```
|
|
140
72
|
|
|
141
|
-
### Useful PM2 Commands
|
|
142
|
-
|
|
143
|
-
```bash
|
|
144
|
-
# View running processes
|
|
145
|
-
pm2 list
|
|
146
|
-
|
|
147
|
-
# View logs
|
|
148
|
-
pm2 logs pglens
|
|
149
|
-
|
|
150
|
-
# Stop pglens
|
|
151
|
-
pm2 stop pglens
|
|
152
|
-
|
|
153
|
-
# Restart pglens
|
|
154
|
-
pm2 restart pglens
|
|
155
|
-
|
|
156
|
-
# Delete pglens from PM2
|
|
157
|
-
pm2 delete pglens
|
|
158
|
-
|
|
159
|
-
# Save PM2 process list (for auto-restart on reboot)
|
|
160
|
-
pm2 save
|
|
161
|
-
|
|
162
|
-
# Setup PM2 to start on system boot
|
|
163
|
-
pm2 startup
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Important Security Reminder
|
|
167
|
-
|
|
168
|
-
⚠️ **Warning**: When running pglens on a server, ensure you:
|
|
169
|
-
|
|
170
|
-
- Use a reverse proxy (nginx, Apache) with authentication
|
|
171
|
-
- Restrict access via firewall rules
|
|
172
|
-
- Use HTTPS/TLS encryption
|
|
173
|
-
- Consider adding authentication middleware
|
|
174
|
-
- Never expose it directly to the internet without proper security measures
|
|
175
|
-
|
|
176
73
|
## How It Works
|
|
177
74
|
|
|
178
|
-
1. **Start
|
|
179
|
-
2. **
|
|
180
|
-
3. **
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
8. **Navigate pages**: Use Previous/Next buttons to load more rows (100 rows per page)
|
|
186
|
-
9. **Refresh data**: Click the refresh button (↻) to reload the current table
|
|
187
|
-
10. **Change theme**: Click the theme button (🌓) to switch between light, dark, or system theme
|
|
75
|
+
1. **Start**: Run `pglens start` to launch the background service
|
|
76
|
+
2. **Connect**: Add one or more database connections via the Web UI
|
|
77
|
+
3. **Explore**:
|
|
78
|
+
- Use the sidebar to browse tables across different connections
|
|
79
|
+
- Double-click cells to view detailed content
|
|
80
|
+
- Use the "Columns" menu to toggle visibility
|
|
81
|
+
- Switch themes for comfortable viewing
|
|
188
82
|
|
|
189
83
|
## Development
|
|
190
84
|
|
|
191
85
|
To develop or modify pglens:
|
|
192
86
|
|
|
193
87
|
```bash
|
|
194
|
-
# Clone
|
|
88
|
+
# Clone the repository
|
|
89
|
+
git clone https://github.com/tsvillain/pglens.git
|
|
195
90
|
cd pglens
|
|
196
91
|
|
|
197
92
|
# Install dependencies
|
|
198
93
|
npm install
|
|
199
94
|
|
|
200
|
-
# Run locally
|
|
201
|
-
node bin/pglens
|
|
95
|
+
# Run locally (starts server in foreground for logs)
|
|
96
|
+
node bin/pglens serve
|
|
202
97
|
```
|
|
203
98
|
|
|
204
99
|
## Contributing
|
|
@@ -210,11 +105,9 @@ Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTIN
|
|
|
210
105
|
- Pull request process
|
|
211
106
|
- Issue reporting
|
|
212
107
|
|
|
213
|
-
We appreciate all contributions, whether it's bug fixes, new features, documentation improvements, or feedback.
|
|
214
|
-
|
|
215
108
|
## Security Note
|
|
216
109
|
|
|
217
|
-
This tool is designed for local development use
|
|
110
|
+
This tool is designed for local development use. While it supports SSL for database connections, the web interface itself runs on HTTP (localhost) and has no user authentication. **Do not expose the pglens port (54321) directly to the internet.**
|
|
218
111
|
|
|
219
112
|
## License
|
|
220
113
|
|
package/bin/pglens
CHANGED
|
@@ -2,17 +2,153 @@
|
|
|
2
2
|
|
|
3
3
|
const { program } = require('commander');
|
|
4
4
|
const { startServer } = require('../src/server');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
|
|
10
|
+
const PID_FILE = path.join(os.homedir(), '.pglens.pid');
|
|
11
|
+
const PORT_FILE = path.join(os.homedir(), '.pglens.port');
|
|
5
12
|
|
|
6
13
|
program
|
|
7
14
|
.name('pglens')
|
|
8
15
|
.description('A simple PostgreSQL database viewer tool')
|
|
9
|
-
.version('
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
.
|
|
13
|
-
|
|
16
|
+
.version('2.0.1');
|
|
17
|
+
|
|
18
|
+
function getRunningPort() {
|
|
19
|
+
if (fs.existsSync(PORT_FILE)) {
|
|
20
|
+
try {
|
|
21
|
+
return parseInt(fs.readFileSync(PORT_FILE, 'utf8'));
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
program
|
|
30
|
+
.command('start')
|
|
31
|
+
.description('Start the pglens server in background')
|
|
32
|
+
.action(async () => {
|
|
33
|
+
// Check if already running
|
|
34
|
+
if (fs.existsSync(PID_FILE)) {
|
|
35
|
+
try {
|
|
36
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8'));
|
|
37
|
+
process.kill(pid, 0); // Check if process exists
|
|
38
|
+
console.log(`pglens is already running (PID: ${pid})`);
|
|
39
|
+
|
|
40
|
+
const port = getRunningPort();
|
|
41
|
+
if (port) {
|
|
42
|
+
console.log(`✓ Server running on http://localhost:${port}`);
|
|
43
|
+
} else {
|
|
44
|
+
console.log(' Server URL unknown (check logs)');
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
} catch (e) {
|
|
48
|
+
// Stale PID file, remove it
|
|
49
|
+
fs.unlinkSync(PID_FILE);
|
|
50
|
+
if (fs.existsSync(PORT_FILE)) fs.unlinkSync(PORT_FILE);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Spawn detached child process
|
|
55
|
+
const child = spawn(process.execPath, [__filename, 'serve'], {
|
|
56
|
+
detached: true,
|
|
57
|
+
stdio: 'ignore'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Save PID
|
|
61
|
+
fs.writeFileSync(PID_FILE, child.pid.toString());
|
|
62
|
+
|
|
63
|
+
// Unref to allow parent to exit
|
|
64
|
+
child.unref();
|
|
65
|
+
|
|
66
|
+
console.log(` Background process started (PID: ${child.pid})`);
|
|
67
|
+
|
|
68
|
+
// Wait for server to start and acquire port
|
|
69
|
+
let attempts = 0;
|
|
70
|
+
const maxAttempts = 20;
|
|
71
|
+
const checkInterval = 100;
|
|
72
|
+
|
|
73
|
+
process.stdout.write(' Waiting for server...');
|
|
74
|
+
|
|
75
|
+
const interval = setInterval(() => {
|
|
76
|
+
const port = getRunningPort();
|
|
77
|
+
if (port) {
|
|
78
|
+
clearInterval(interval);
|
|
79
|
+
process.stdout.write('\r\x1b[K'); // Clear line
|
|
80
|
+
console.log(`✓ Server running on http://localhost:${port}`);
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
attempts++;
|
|
85
|
+
if (attempts >= maxAttempts) {
|
|
86
|
+
clearInterval(interval);
|
|
87
|
+
process.stdout.write('\r\x1b[K'); // Clear line
|
|
88
|
+
console.log('✓ Server started (could not determine port, check logs)');
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
}, checkInterval);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Hidden command that actually runs the server
|
|
95
|
+
program
|
|
96
|
+
.command('serve', { hidden: true })
|
|
97
|
+
.action(() => {
|
|
98
|
+
startServer();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
program
|
|
102
|
+
.command('stop')
|
|
103
|
+
.description('Stop the pglens server')
|
|
104
|
+
.action(() => {
|
|
105
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
106
|
+
console.log('pglens is not running');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8'));
|
|
112
|
+
process.kill(pid, 'SIGTERM');
|
|
113
|
+
fs.unlinkSync(PID_FILE);
|
|
114
|
+
if (fs.existsSync(PORT_FILE)) fs.unlinkSync(PORT_FILE);
|
|
115
|
+
console.log('pglens stopped');
|
|
116
|
+
} catch (e) {
|
|
117
|
+
console.error(`Error stopping pglens: ${e.message}`);
|
|
118
|
+
// Clean up if process not found
|
|
119
|
+
if (e.code === 'ESRCH') {
|
|
120
|
+
fs.unlinkSync(PID_FILE);
|
|
121
|
+
if (fs.existsSync(PORT_FILE)) fs.unlinkSync(PORT_FILE);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
program
|
|
127
|
+
.command('url')
|
|
128
|
+
.description('Show the URL where pglens is running')
|
|
129
|
+
.action(() => {
|
|
130
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
131
|
+
console.log('pglens is not running');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
14
134
|
|
|
15
|
-
|
|
135
|
+
// Verify process is actually running
|
|
136
|
+
try {
|
|
137
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8'));
|
|
138
|
+
process.kill(pid, 0);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
console.log('pglens is not running (stale PID file)');
|
|
141
|
+
fs.unlinkSync(PID_FILE);
|
|
142
|
+
if (fs.existsSync(PORT_FILE)) fs.unlinkSync(PORT_FILE);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
16
145
|
|
|
17
|
-
|
|
146
|
+
const port = getRunningPort();
|
|
147
|
+
if (port) {
|
|
148
|
+
console.log(`http://localhost:${port}`);
|
|
149
|
+
} else {
|
|
150
|
+
console.log('Could not determine server URL.');
|
|
151
|
+
}
|
|
152
|
+
});
|
|
18
153
|
|
|
154
|
+
program.parse(process.argv);
|