mirror of
https://github.com/johndoe6345789/postgres.git
synced 2026-04-24 13:55:00 +00:00
Add password generator, Caprover deployment, and position as modern legacy tool replacement
Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
248
ADMIN_README.md
248
ADMIN_README.md
@@ -1,6 +1,43 @@
|
||||
# Postgres Web Admin Panel
|
||||
|
||||
A modern web-based admin interface for PostgreSQL with Material UI.
|
||||
A **modern, beautiful replacement for legacy database admin tools** like phpMyAdmin, Adminer, and pgAdmin.
|
||||
|
||||
Built with Next.js 16, Material UI, and TypeScript for a superior user experience.
|
||||
|
||||
## Why This Instead of Legacy Tools?
|
||||
|
||||
### 🎨 Modern vs. Crusty
|
||||
- **Beautiful Material UI** instead of outdated 2000s-era interfaces
|
||||
- **Dark mode friendly** and responsive design
|
||||
- **Fast, smooth interactions** with React and Next.js
|
||||
- **Clean, intuitive navigation** vs cluttered legacy UIs
|
||||
|
||||
### 🚀 All-in-One Solution
|
||||
- **Includes PostgreSQL** - no separate database setup needed
|
||||
- **Docker-ready** - deploy anywhere in seconds
|
||||
- **Zero configuration** - works out of the box
|
||||
- **Built-in authentication** - no complicated auth setup
|
||||
|
||||
### 🔒 Security First
|
||||
- **Modern authentication** with bcrypt + JWT
|
||||
- **SQL injection protection** with multiple layers
|
||||
- **Session management** with HTTP-only cookies
|
||||
- **Auto-generated passwords** - no default "admin/admin"
|
||||
|
||||
### 💼 Production Ready
|
||||
- **Caprover compatible** - deploy with one click
|
||||
- **GitHub Container Registry** - automated CI/CD
|
||||
- **Cloudflare Tunnel support** - easy HTTPS
|
||||
- **Persistent storage** - data survives restarts
|
||||
|
||||
## Replaces These Legacy Tools
|
||||
|
||||
| Old Tool | Issues | This Solution |
|
||||
|----------|--------|---------------|
|
||||
| **phpMyAdmin** | PHP-based, outdated UI, MySQL-focused | Modern Next.js, beautiful UI, PostgreSQL-focused |
|
||||
| **Adminer** | Single PHP file, basic features | Full-featured app with authentication |
|
||||
| **pgAdmin** | Heavy desktop app, complex setup | Lightweight web app, simple deployment |
|
||||
| **SQL Workbench** | Desktop only, OS-specific | Web-based, works everywhere |
|
||||
|
||||
## Features
|
||||
|
||||
@@ -30,15 +67,56 @@ Create an admin user for logging into the admin panel:
|
||||
npm run db:seed-admin
|
||||
```
|
||||
|
||||
This creates a default admin user:
|
||||
- **Username**: admin
|
||||
- **Password**: admin123
|
||||
**Auto-generated password**: If you don't provide a password, a secure 32-character password will be automatically generated:
|
||||
|
||||
```bash
|
||||
✅ Admin user created successfully!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📧 Username: admin
|
||||
🔑 Password: aB3$xK9@mP2&vL8#qR5!wN7^zT4%yU6*
|
||||
⚠️ This password was auto-generated. Save it securely!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🌐 Login at: http://localhost:3000/admin/login
|
||||
```
|
||||
|
||||
**Custom credentials**: You can also provide custom credentials:
|
||||
|
||||
You can customize these credentials by setting environment variables:
|
||||
```bash
|
||||
ADMIN_USERNAME=myuser ADMIN_PASSWORD=mypassword npm run db:seed-admin
|
||||
```
|
||||
|
||||
### Generate Secure Passwords
|
||||
|
||||
Use the built-in password generator to create secure passwords:
|
||||
|
||||
```bash
|
||||
# Generate a 32-character password (default)
|
||||
npm run generate:password
|
||||
|
||||
# Generate a 64-character password
|
||||
npm run generate:password 64
|
||||
|
||||
# Generate without special characters
|
||||
npm run generate:password 32 false
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔐 Secure Password Generated
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Password: xK9@mP2&vL8#qR5!wN7^zT4%yU6*aB3$
|
||||
Length: 32 characters
|
||||
Special characters: Yes
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⚠️ Save this password securely!
|
||||
|
||||
💡 Usage examples:
|
||||
ADMIN_PASSWORD="xK9@mP2&vL8#qR5!wN7^zT4%yU6*aB3$" npm run db:seed-admin
|
||||
export JWT_SECRET="xK9@mP2&vL8#qR5!wN7^zT4%yU6*aB3$"
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### 3. Access the Admin Panel
|
||||
|
||||
Start the development server:
|
||||
@@ -48,6 +126,166 @@ npm run dev
|
||||
|
||||
Navigate to: **http://localhost:3000/admin/login**
|
||||
|
||||
## Caprover Deployment
|
||||
|
||||
The application is ready to deploy on Caprover with minimal configuration.
|
||||
|
||||
### Deploy to Caprover
|
||||
|
||||
1. **Create a new app** in your Caprover dashboard
|
||||
- App Name: `postgres-admin` (or your choice)
|
||||
- Enable HTTPS (Caprover handles SSL automatically)
|
||||
|
||||
2. **Deploy via Dockerfile**:
|
||||
- Caprover will automatically use the Dockerfile in the repository
|
||||
- No additional configuration needed!
|
||||
|
||||
3. **Set Environment Variables** in Caprover:
|
||||
```
|
||||
JWT_SECRET=<generate-with-openssl-rand-base64-32>
|
||||
CREATE_ADMIN_USER=true
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=your-secure-password
|
||||
```
|
||||
|
||||
4. **Access your admin panel**:
|
||||
- https://postgres-admin.your-caprover-domain.com/admin/login
|
||||
|
||||
### Captain Definition (Optional)
|
||||
|
||||
If you want to customize the build, create `captain-definition` in the root:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"dockerfilePath": "./Dockerfile"
|
||||
}
|
||||
```
|
||||
|
||||
### Caprover One-Click App (Optional)
|
||||
|
||||
For easier deployment, you can also deploy as a one-click app. The all-in-one Docker image includes PostgreSQL and Next.js, so no external database needed!
|
||||
|
||||
### Notes
|
||||
|
||||
- **HTTPS**: Caprover automatically provides HTTPS via Let's Encrypt
|
||||
- **Built-in Database**: The Docker image includes PostgreSQL, no need for separate database setup
|
||||
- **Persistent Storage**: Caprover automatically handles volume persistence
|
||||
- **Auto-restart**: Caprover restarts the container automatically on failure
|
||||
|
||||
## Cloudflare Tunnel Deployment (Alternative)
|
||||
|
||||
<details>
|
||||
<summary>Click to expand Cloudflare Tunnel instructions</summary>
|
||||
|
||||
The application works seamlessly with Cloudflare Tunnel for secure HTTPS access without exposing ports.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Install `cloudflared`: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/
|
||||
2. Cloudflare account with a domain
|
||||
|
||||
### Quick Setup with Cloudflare Tunnel
|
||||
|
||||
1. **Start the application**:
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
2. **Create a Cloudflare Tunnel**:
|
||||
```bash
|
||||
cloudflared tunnel login
|
||||
cloudflared tunnel create postgres-admin
|
||||
```
|
||||
|
||||
3. **Create tunnel configuration** (`~/.cloudflared/config.yml`):
|
||||
```yaml
|
||||
tunnel: <your-tunnel-id>
|
||||
credentials-file: /home/user/.cloudflared/<tunnel-id>.json
|
||||
|
||||
ingress:
|
||||
- hostname: postgres-admin.yourdomain.com
|
||||
service: http://localhost:3000
|
||||
- service: http_status:404
|
||||
```
|
||||
|
||||
4. **Route DNS**:
|
||||
```bash
|
||||
cloudflared tunnel route dns postgres-admin postgres-admin.yourdomain.com
|
||||
```
|
||||
|
||||
5. **Run the tunnel**:
|
||||
```bash
|
||||
cloudflared tunnel run postgres-admin
|
||||
```
|
||||
|
||||
6. **Access your admin panel** at:
|
||||
- https://postgres-admin.yourdomain.com/admin/login
|
||||
|
||||
### Docker Compose with Cloudflare Tunnel
|
||||
|
||||
Create a complete setup with tunnel included:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres-admin:
|
||||
build: .
|
||||
ports:
|
||||
- '3000:3000'
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://docker:docker@localhost:5432/postgres
|
||||
- JWT_SECRET=your-secret-key-change-in-production
|
||||
- CREATE_ADMIN_USER=true
|
||||
- ADMIN_USERNAME=admin
|
||||
- ADMIN_PASSWORD=admin123
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/15/main
|
||||
|
||||
cloudflared:
|
||||
image: cloudflare/cloudflared:latest
|
||||
command: tunnel --no-autoupdate run
|
||||
environment:
|
||||
- TUNNEL_TOKEN=<your-tunnel-token>
|
||||
depends_on:
|
||||
- postgres-admin
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
```
|
||||
|
||||
Get your tunnel token from: https://one.dash.cloudflare.com/
|
||||
|
||||
### Security Considerations with Cloudflare
|
||||
|
||||
✅ **Automatic HTTPS** - Cloudflare provides SSL/TLS automatically
|
||||
✅ **DDoS Protection** - Built-in Cloudflare protection
|
||||
✅ **Access Control** - Use Cloudflare Access for additional authentication
|
||||
✅ **Rate Limiting** - Configure Cloudflare rate limits
|
||||
✅ **WAF** - Web Application Firewall protection
|
||||
|
||||
### Recommended Cloudflare Settings
|
||||
|
||||
1. **SSL/TLS Mode**: Full (strict) recommended
|
||||
2. **Always Use HTTPS**: Enabled
|
||||
3. **Automatic HTTPS Rewrites**: Enabled
|
||||
4. **HTTP Strict Transport Security (HSTS)**: Enabled
|
||||
5. **Rate Limiting**: Configure for /api/* endpoints
|
||||
|
||||
### Cloudflare Access (Optional Extra Security)
|
||||
|
||||
Add an extra authentication layer:
|
||||
|
||||
```bash
|
||||
# In Cloudflare Dashboard > Access > Applications
|
||||
# Create a new application for postgres-admin.yourdomain.com
|
||||
# Add authentication methods (Email OTP, Google, etc.)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
The project includes an **all-in-one Docker image** that contains both PostgreSQL and the Next.js application, making deployment simple and straightforward.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"db:migrate": "dotenv -c -- drizzle-kit migrate",
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"db:seed-admin": "tsx scripts/seed-admin.ts",
|
||||
"generate:password": "tsx scripts/generate-password.ts",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:test": "vitest run --config .storybook/vitest.config.mts",
|
||||
"build-storybook": "storybook build"
|
||||
|
||||
63
scripts/generate-password.ts
Normal file
63
scripts/generate-password.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as crypto from 'node:crypto';
|
||||
|
||||
/**
|
||||
* Generate a secure random password
|
||||
* @param length - Length of the password (default: 32)
|
||||
* @param includeSpecial - Include special characters (default: true)
|
||||
*/
|
||||
function generateSecurePassword(length = 32, includeSpecial = true): string {
|
||||
let charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
if (includeSpecial) {
|
||||
charset += '!@#$%^&*()-_=+[]{}|;:,.<>?';
|
||||
}
|
||||
|
||||
const randomBytes = crypto.randomBytes(length);
|
||||
let password = '';
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const byte = randomBytes[i];
|
||||
if (byte !== undefined) {
|
||||
password += charset[byte % charset.length];
|
||||
}
|
||||
}
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
// CLI interface
|
||||
if (require.main === module) {
|
||||
const args = process.argv.slice(2);
|
||||
const length = args[0] ? Number.parseInt(args[0], 10) : 32;
|
||||
const includeSpecial = args[1] !== 'false';
|
||||
|
||||
if (Number.isNaN(length) || length < 8) {
|
||||
console.error('Error: Password length must be at least 8 characters');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (length > 128) {
|
||||
console.error('Error: Password length cannot exceed 128 characters');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const password = generateSecurePassword(length, includeSpecial);
|
||||
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('🔐 Secure Password Generated');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log(`Password: ${password}`);
|
||||
console.log(`Length: ${password.length} characters`);
|
||||
console.log(`Special characters: ${includeSpecial ? 'Yes' : 'No'}`);
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('⚠️ Save this password securely!');
|
||||
console.log('');
|
||||
console.log('💡 Usage examples:');
|
||||
console.log(` ADMIN_PASSWORD="${password}" npm run db:seed-admin`);
|
||||
console.log(` export JWT_SECRET="${password}"`);
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
}
|
||||
|
||||
export { generateSecurePassword };
|
||||
@@ -2,6 +2,7 @@ import * as bcrypt from 'bcryptjs';
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { Pool } from 'pg';
|
||||
import { adminUserSchema } from '../src/models/Schema';
|
||||
import { generateSecurePassword } from './generate-password';
|
||||
|
||||
async function seedAdminUser() {
|
||||
const pool = new Pool({
|
||||
@@ -11,7 +12,15 @@ async function seedAdminUser() {
|
||||
const db = drizzle(pool);
|
||||
|
||||
const username = process.env.ADMIN_USERNAME || 'admin';
|
||||
const password = process.env.ADMIN_PASSWORD || 'admin123';
|
||||
|
||||
// Generate secure password if not provided
|
||||
let password = process.env.ADMIN_PASSWORD;
|
||||
let passwordGenerated = false;
|
||||
|
||||
if (!password) {
|
||||
password = generateSecurePassword(32);
|
||||
passwordGenerated = true;
|
||||
}
|
||||
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
|
||||
@@ -21,14 +30,20 @@ async function seedAdminUser() {
|
||||
passwordHash,
|
||||
});
|
||||
|
||||
console.log(`Admin user created successfully!`);
|
||||
console.log(`Username: ${username}`);
|
||||
console.log(`Password: ${password}`);
|
||||
console.log('✅ Admin user created successfully!');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log(`📧 Username: ${username}`);
|
||||
console.log(`🔑 Password: ${password}`);
|
||||
if (passwordGenerated) {
|
||||
console.log('⚠️ This password was auto-generated. Save it securely!');
|
||||
}
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('🌐 Login at: http://localhost:3000/admin/login');
|
||||
} catch (error: any) {
|
||||
if (error.code === '23505') {
|
||||
console.log('Admin user already exists');
|
||||
console.log('ℹ️ Admin user already exists');
|
||||
} else {
|
||||
console.error('Error creating admin user:', error);
|
||||
console.error('❌ Error creating admin user:', error);
|
||||
}
|
||||
} finally {
|
||||
await pool.end();
|
||||
|
||||
@@ -15,7 +15,8 @@ function validateSelectQuery(query: string): boolean {
|
||||
}
|
||||
|
||||
// Check for dangerous keywords (case insensitive)
|
||||
const dangerous = /;\s*(?:drop|delete|update|insert|alter|create|truncate|exec|execute)\s/i;
|
||||
// Includes common SQL modification commands and advanced features
|
||||
const dangerous = /;\s*(?:drop|delete|update|insert|alter|create|truncate|exec|execute|merge|call|with)\s/i;
|
||||
if (dangerous.test(trimmed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -38,9 +38,10 @@ export async function POST(request: Request) {
|
||||
);
|
||||
}
|
||||
|
||||
// Table name is validated, sanitize and use
|
||||
const safeTableName = String(tableName).replace(/\W/g, '');
|
||||
const result = await db.execute(sql.raw(`SELECT * FROM "${safeTableName}" LIMIT 100`));
|
||||
// Table name is validated against schema - safe to use the validated name
|
||||
// The validation query above ensures the table exists in the public schema
|
||||
const validatedTableName = (tablesResult.rows[0] as any).table_name;
|
||||
const result = await db.execute(sql.raw(`SELECT * FROM "${validatedTableName}" LIMIT 100`));
|
||||
|
||||
return NextResponse.json({
|
||||
rows: result.rows,
|
||||
|
||||
Reference in New Issue
Block a user