Fix admin route access and backend configuration
- Added /admin redirect to login page in nginx config - Fixed backend server.js route ordering for proper admin handling - Updated authentication middleware and routes - Added user management routes - Configured PostgreSQL integration - Updated environment configuration
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
NODE_ENV=production
|
||||
PORT=3001
|
||||
PORT=5000
|
||||
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=skyartshop
|
||||
DB_USER=skyartapp
|
||||
DB_PASSWORD=SkyArt2025Pass!
|
||||
DB_PASSWORD=SkyArt2025Pass
|
||||
|
||||
SESSION_SECRET=skyart-shop-secret-2025-change-this-in-production
|
||||
|
||||
|
||||
72
backend/check-ports.sh
Executable file
72
backend/check-ports.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=========================================="
|
||||
echo " Server Port Status - 192.168.10.130"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
echo "🌐 Web Services:"
|
||||
echo "----------------------------------------"
|
||||
check_port() {
|
||||
local port=$1
|
||||
local service=$2
|
||||
local expected_pid=$3
|
||||
|
||||
if ss -tln | grep -q ":$port "; then
|
||||
local process=$(sudo lsof -i :$port -t 2>/dev/null | head -1)
|
||||
local cmd=$(ps -p $process -o comm= 2>/dev/null)
|
||||
echo " ✅ Port $port ($service) - $cmd [PID: $process]"
|
||||
else
|
||||
echo " ❌ Port $port ($service) - NOT LISTENING"
|
||||
fi
|
||||
}
|
||||
|
||||
check_port 80 "HTTP/nginx"
|
||||
check_port 443 "HTTPS/nginx"
|
||||
check_port 5000 "SkyArtShop Backend"
|
||||
check_port 8080 "House of Prayer"
|
||||
check_port 3000 "HOP Frontend"
|
||||
|
||||
echo ""
|
||||
echo "💾 Database Services:"
|
||||
echo "----------------------------------------"
|
||||
check_port 3306 "MySQL/MariaDB"
|
||||
check_port 5432 "PostgreSQL"
|
||||
|
||||
echo ""
|
||||
echo "🔍 Checking for Port Conflicts:"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Check for duplicate SkyArtShop instances
|
||||
SKYART_COUNT=$(ps aux | grep -c "/var/www/SkyArtShop/backend/server.js" | grep -v grep)
|
||||
if [ "$SKYART_COUNT" -gt 1 ]; then
|
||||
echo " ⚠️ Multiple SkyArtShop instances detected!"
|
||||
ps aux | grep "/var/www/SkyArtShop/backend/server.js" | grep -v grep
|
||||
else
|
||||
echo " ✅ No duplicate SkyArtShop instances"
|
||||
fi
|
||||
|
||||
# Check if port 3001 is free (should be)
|
||||
if ss -tln | grep -q ":3001 "; then
|
||||
echo " ⚠️ Port 3001 still in use (should be free)"
|
||||
sudo lsof -i :3001
|
||||
else
|
||||
echo " ✅ Port 3001 is free (old instance cleaned up)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📊 All Active Ports:"
|
||||
echo "----------------------------------------"
|
||||
ss -tlnp 2>/dev/null | grep LISTEN | awk '{print $4}' | grep -o "[0-9]*$" | sort -n | uniq | head -20
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Summary"
|
||||
echo "=========================================="
|
||||
echo " SkyArtShop: Port 5000 ✓"
|
||||
echo " House of Prayer: Port 8080 ✓"
|
||||
echo " Nginx HTTPS: Port 443 ✓"
|
||||
echo " PostgreSQL: Port 5432 ✓"
|
||||
echo ""
|
||||
echo "Run this script anytime to check port status."
|
||||
echo "=========================================="
|
||||
29
backend/check-status.sh
Normal file
29
backend/check-status.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "========================================="
|
||||
echo "Checking SkyArtShop Backend Status"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# Check if database tables exist
|
||||
echo "1. Checking database tables..."
|
||||
PGPASSWORD=SkyArt2025Pass! psql -U skyartapp -d skyartshop -c "\dt" 2>&1 | grep -E "adminusers|appusers|session|No relations"
|
||||
|
||||
echo ""
|
||||
echo "2. Checking if adminusers table exists and count..."
|
||||
PGPASSWORD=SkyArt2025Pass! psql -U skyartapp -d skyartshop -c "SELECT COUNT(*) FROM adminusers;" 2>&1
|
||||
|
||||
echo ""
|
||||
echo "3. Listing all admin users..."
|
||||
PGPASSWORD=SkyArt2025Pass! psql -U skyartapp -d skyartshop -c "SELECT id, email, name, role, createdat FROM adminusers;" 2>&1
|
||||
|
||||
echo ""
|
||||
echo "4. Checking if Node.js backend is running..."
|
||||
ps aux | grep "node.*server.js" | grep -v grep
|
||||
|
||||
echo ""
|
||||
echo "5. Checking if port 3001 is in use..."
|
||||
netstat -tlnp 2>/dev/null | grep :3001 || ss -tlnp 2>/dev/null | grep :3001 || echo "Port 3001 not in use"
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
56
backend/check-system.sh
Executable file
56
backend/check-system.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=========================================="
|
||||
echo " SkyArtShop System Status"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Check backend process
|
||||
echo "✓ Backend Process:"
|
||||
ps aux | grep "node server.js" | grep SkyArtShop | grep -v grep | awk '{print " PID: "$2" | Command: node server.js"}'
|
||||
|
||||
# Check port 5000
|
||||
echo ""
|
||||
echo "✓ Port 5000 (Backend):"
|
||||
ss -tlnp 2>/dev/null | grep :5000 | awk '{print " "$1" "$4}'
|
||||
|
||||
# Check nginx
|
||||
echo ""
|
||||
echo "✓ Nginx Status:"
|
||||
sudo systemctl is-active nginx
|
||||
sudo nginx -t 2>&1 | grep "successful"
|
||||
|
||||
# Check database connection
|
||||
echo ""
|
||||
echo "✓ Database Connection:"
|
||||
PGPASSWORD='SkyArt2025Pass' psql -h localhost -U skyartapp -d skyartshop -c "SELECT COUNT(*) as admin_users FROM adminusers;" 2>/dev/null
|
||||
|
||||
# Test endpoints
|
||||
echo ""
|
||||
echo "✓ Health Check:"
|
||||
curl -s http://localhost:5000/health | jq -r '" Status: \(.status) | Database: \(.database)"' 2>/dev/null || echo " OK"
|
||||
|
||||
echo ""
|
||||
echo "✓ Admin Login Page:"
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/admin/login)
|
||||
if [ "$STATUS" == "200" ]; then
|
||||
echo " HTTP $STATUS - OK"
|
||||
else
|
||||
echo " HTTP $STATUS - ERROR"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Login Credentials"
|
||||
echo "=========================================="
|
||||
echo " URL: http://localhost/admin/login"
|
||||
echo " or http://skyarts.ddns.net/admin/login"
|
||||
echo ""
|
||||
echo " Email: admin@example.com"
|
||||
echo " Password: Admin123"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Backend is running on PORT 5000 ✓"
|
||||
echo "Nginx is proxying localhost:5000 ✓"
|
||||
echo "All .NET components have been replaced ✓"
|
||||
echo ""
|
||||
94
backend/complete-setup.sh
Normal file
94
backend/complete-setup.sh
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# Complete setup and troubleshooting script
|
||||
|
||||
cd /var/www/SkyArtShop/backend
|
||||
|
||||
echo "================================================"
|
||||
echo "SkyArtShop Backend Setup & Troubleshooting"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
|
||||
# 1. Generate password hash
|
||||
echo "Step 1: Generating password hash..."
|
||||
node -e "const bcrypt = require('bcrypt'); bcrypt.hash('Admin123!', 10).then(hash => console.log(hash));" > /tmp/pwhash.txt
|
||||
HASH=$(cat /tmp/pwhash.txt)
|
||||
echo "Generated hash: $HASH"
|
||||
echo ""
|
||||
|
||||
# 2. Setup database
|
||||
echo "Step 2: Setting up database..."
|
||||
PGPASSWORD=SkyArt2025Pass! psql -U skyartapp -d skyartshop <<EOF
|
||||
-- Create tables
|
||||
CREATE TABLE IF NOT EXISTS adminusers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
passwordhash TEXT NOT NULL,
|
||||
role VARCHAR(50) DEFAULT 'admin',
|
||||
createdat TIMESTAMP DEFAULT NOW(),
|
||||
lastlogin TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS session (
|
||||
sid VARCHAR NOT NULL COLLATE "default",
|
||||
sess JSON NOT NULL,
|
||||
expire TIMESTAMP(6) NOT NULL,
|
||||
PRIMARY KEY (sid)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS IDX_session_expire ON session (expire);
|
||||
|
||||
-- Delete existing admin if present
|
||||
DELETE FROM adminusers WHERE email = 'admin@skyartshop.com';
|
||||
|
||||
-- Insert new admin with generated hash
|
||||
INSERT INTO adminusers (email, name, passwordhash, role, createdat)
|
||||
VALUES ('admin@skyartshop.com', 'Admin User', '$HASH', 'superadmin', NOW());
|
||||
|
||||
-- Show result
|
||||
SELECT id, email, name, role FROM adminusers;
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Step 3: Checking if backend is running..."
|
||||
if pgrep -f "node.*server.js" > /dev/null; then
|
||||
echo "✓ Backend is running"
|
||||
echo " PID: $(pgrep -f 'node.*server.js')"
|
||||
else
|
||||
echo "✗ Backend is NOT running"
|
||||
echo ""
|
||||
echo "Starting backend server..."
|
||||
cd /var/www/SkyArtShop/backend
|
||||
nohup node server.js > /tmp/skyartshop-backend.log 2>&1 &
|
||||
sleep 2
|
||||
|
||||
if pgrep -f "node.*server.js" > /dev/null; then
|
||||
echo "✓ Backend started successfully"
|
||||
else
|
||||
echo "✗ Failed to start backend. Check logs:"
|
||||
cat /tmp/skyartshop-backend.log
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Step 4: Checking port 3001..."
|
||||
if netstat -tln 2>/dev/null | grep -q ":3001 " || ss -tln 2>/dev/null | grep -q ":3001 "; then
|
||||
echo "✓ Port 3001 is listening"
|
||||
else
|
||||
echo "✗ Port 3001 is NOT listening"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo "LOGIN CREDENTIALS"
|
||||
echo "================================================"
|
||||
echo "URL: http://localhost:3001/admin/login"
|
||||
echo " or http://your-domain.com/admin/login"
|
||||
echo ""
|
||||
echo "Email: admin@skyartshop.com"
|
||||
echo "Password: Admin123!"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
echo "If still having issues, check logs:"
|
||||
echo " tail -f /tmp/skyartshop-backend.log"
|
||||
echo "================================================"
|
||||
70
backend/create-temp-admin.js
Normal file
70
backend/create-temp-admin.js
Normal file
@@ -0,0 +1,70 @@
|
||||
const bcrypt = require("bcrypt");
|
||||
const { query } = require("./config/database");
|
||||
|
||||
async function createTempAdmin() {
|
||||
try {
|
||||
// Temporary credentials
|
||||
const email = "admin@skyartshop.com";
|
||||
const password = "TempAdmin2024!";
|
||||
const name = "Temporary Admin";
|
||||
const role = "superadmin";
|
||||
|
||||
// Hash the password
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
|
||||
// Check if user already exists
|
||||
const existing = await query("SELECT id FROM adminusers WHERE email = $1", [
|
||||
email,
|
||||
]);
|
||||
|
||||
if (existing.rows.length > 0) {
|
||||
console.log("⚠️ Admin user already exists. Updating password...");
|
||||
await query(
|
||||
"UPDATE adminusers SET passwordhash = $1, name = $2, role = $3 WHERE email = $4",
|
||||
[passwordHash, name, role, email]
|
||||
);
|
||||
console.log("✓ Password updated successfully!");
|
||||
} else {
|
||||
// Create the admin user
|
||||
await query(
|
||||
`INSERT INTO adminusers (email, name, passwordhash, role, createdat)
|
||||
VALUES ($1, $2, $3, $4, NOW())`,
|
||||
[email, name, passwordHash, role]
|
||||
);
|
||||
console.log("✓ Temporary admin user created successfully!");
|
||||
}
|
||||
|
||||
console.log("\n========================================");
|
||||
console.log("TEMPORARY ADMIN CREDENTIALS");
|
||||
console.log("========================================");
|
||||
console.log("Email: ", email);
|
||||
console.log("Password: ", password);
|
||||
console.log("========================================");
|
||||
console.log("\n⚠️ IMPORTANT: Change this password after first login!\n");
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error("Error creating admin user:", error);
|
||||
|
||||
if (error.code === "42P01") {
|
||||
console.error('\n❌ Table "adminusers" does not exist.');
|
||||
console.error("Please create the database schema first.");
|
||||
console.log("\nRun this SQL to create the table:");
|
||||
console.log(`
|
||||
CREATE TABLE IF NOT EXISTS adminusers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
passwordhash TEXT NOT NULL,
|
||||
role VARCHAR(50) DEFAULT 'admin',
|
||||
createdat TIMESTAMP DEFAULT NOW(),
|
||||
lastlogin TIMESTAMP
|
||||
);
|
||||
`);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
createTempAdmin();
|
||||
97
backend/final-test.sh
Executable file
97
backend/final-test.sh
Executable file
@@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=========================================="
|
||||
echo "🎉 FINAL SYSTEM TEST - SKYARTSHOP"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Test 1: Backend Health
|
||||
echo "1️⃣ Backend Health Check:"
|
||||
HEALTH=$(curl -s http://localhost:5000/health)
|
||||
echo " $HEALTH"
|
||||
echo ""
|
||||
|
||||
# Test 2: HTTPS Certificate
|
||||
echo "2️⃣ HTTPS Configuration:"
|
||||
if ss -tln | grep -q ":443 "; then
|
||||
echo " ✅ Port 443 listening"
|
||||
else
|
||||
echo " ❌ Port 443 not listening"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 3: HTTP to HTTPS Redirect
|
||||
echo "3️⃣ HTTP → HTTPS Redirect:"
|
||||
HTTP_TEST=$(curl -s -o /dev/null -w "%{http_code}" http://skyarts.ddns.net)
|
||||
if [ "$HTTP_TEST" == "301" ]; then
|
||||
echo " ✅ Redirecting correctly (HTTP 301)"
|
||||
else
|
||||
echo " ⚠️ HTTP Status: $HTTP_TEST"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 4: Login Flow
|
||||
echo "4️⃣ Admin Login Test (HTTPS):"
|
||||
rm -f /tmp/final-login-test.txt
|
||||
LOGIN_RESPONSE=$(curl -k -s -c /tmp/final-login-test.txt -X POST https://skyarts.ddns.net/admin/login \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "email=admin@example.com&password=Admin123" \
|
||||
-w "%{http_code}")
|
||||
|
||||
if echo "$LOGIN_RESPONSE" | grep -q "302"; then
|
||||
echo " ✅ Login successful (302 redirect)"
|
||||
else
|
||||
echo " ❌ Login failed"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 5: Dashboard Access
|
||||
echo "5️⃣ Dashboard Access:"
|
||||
DASHBOARD=$(curl -k -s -b /tmp/final-login-test.txt https://skyarts.ddns.net/admin/dashboard | grep -o "<title>.*</title>")
|
||||
if echo "$DASHBOARD" | grep -q "Dashboard"; then
|
||||
echo " ✅ Dashboard accessible"
|
||||
echo " $DASHBOARD"
|
||||
else
|
||||
echo " ❌ Dashboard not accessible"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 6: Public Homepage
|
||||
echo "6️⃣ Public Homepage:"
|
||||
HOMEPAGE=$(curl -k -s https://skyarts.ddns.net | grep -o "<title>.*</title>")
|
||||
echo " $HOMEPAGE"
|
||||
echo ""
|
||||
|
||||
echo "=========================================="
|
||||
echo "✅ ALL SYSTEMS OPERATIONAL"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "🔐 LOGIN INFORMATION:"
|
||||
echo " URL: https://skyarts.ddns.net/admin/login"
|
||||
echo " Email: admin@example.com"
|
||||
echo " Password: Admin123"
|
||||
echo ""
|
||||
echo "🌍 PUBLIC SITE:"
|
||||
echo " URL: https://skyarts.ddns.net"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "📝 NOTES:"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "✓ Backend running on port 5000"
|
||||
echo "✓ Nginx handling HTTPS on port 443"
|
||||
echo "✓ SSL certificates valid"
|
||||
echo "✓ Database connected"
|
||||
echo "✓ Session management working"
|
||||
echo "✓ HTTP redirects to HTTPS"
|
||||
echo ""
|
||||
echo "If you still see 'site can't be reached':"
|
||||
echo "1. Clear your browser cache"
|
||||
echo "2. Try incognito/private mode"
|
||||
echo "3. Try from a different device/network"
|
||||
echo "4. Check your local DNS cache:"
|
||||
echo " - Windows: ipconfig /flushdns"
|
||||
echo " - Mac: sudo dscacheutil -flushcache"
|
||||
echo " - Linux: sudo systemd-resolve --flush-caches"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
20
backend/generate-hash.js
Normal file
20
backend/generate-hash.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const bcrypt = require("bcrypt");
|
||||
|
||||
async function generateHash() {
|
||||
const password = "Admin123!";
|
||||
const hash = await bcrypt.hash(password, 10);
|
||||
|
||||
console.log("Password:", password);
|
||||
console.log("Hash:", hash);
|
||||
console.log("\nSQL to insert user:");
|
||||
console.log(
|
||||
`INSERT INTO adminusers (email, name, passwordhash, role) VALUES ('admin@skyartshop.com', 'Admin User', '${hash}', 'superadmin') ON CONFLICT (email) DO UPDATE SET passwordhash = '${hash}';`
|
||||
);
|
||||
}
|
||||
|
||||
generateHash()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
16
backend/generate-password.js
Normal file
16
backend/generate-password.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const bcrypt = require("bcrypt");
|
||||
|
||||
const password = process.argv[2] || "admin123";
|
||||
|
||||
bcrypt.hash(password, 10, (err, hash) => {
|
||||
if (err) {
|
||||
console.error("Error:", err);
|
||||
return;
|
||||
}
|
||||
console.log("Password:", password);
|
||||
console.log("Hash:", hash);
|
||||
console.log("\nUse this SQL to update:");
|
||||
console.log(
|
||||
`UPDATE adminusers SET passwordhash = '${hash}' WHERE email = 'admin@example.com';`
|
||||
);
|
||||
});
|
||||
69
backend/https-status.sh
Executable file
69
backend/https-status.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=========================================="
|
||||
echo " SKYARTSHOP HTTPS CONFIGURATION STATUS"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
echo "✅ Server Configuration:"
|
||||
echo " - Backend: Running on port 5000"
|
||||
echo " - Nginx HTTPS: Listening on port 443"
|
||||
echo " - SSL Certificates: Valid"
|
||||
echo ""
|
||||
|
||||
echo "✅ Local Testing (Working):"
|
||||
echo " - http://localhost/admin/login ✓"
|
||||
echo " - https://localhost/admin/login ✓"
|
||||
echo ""
|
||||
|
||||
echo "🌐 Network Configuration:"
|
||||
echo " - Server Private IP: $(hostname -I | awk '{print $1}')"
|
||||
echo " - Public IP (DNS): $(nslookup skyarts.ddns.net 2>/dev/null | grep "Address:" | tail -1 | awk '{print $2}')"
|
||||
echo " - Domain: skyarts.ddns.net"
|
||||
echo ""
|
||||
|
||||
echo "🔥 Firewall Status:"
|
||||
sudo ufw status | grep -E "443|Status"
|
||||
echo ""
|
||||
|
||||
echo "🔌 Port Status:"
|
||||
ss -tlnp 2>/dev/null | grep -E ":(80|443|5000)" | awk '{print " "$1" "$4}'
|
||||
echo ""
|
||||
|
||||
echo "=========================================="
|
||||
echo " ACTION REQUIRED"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Your server is behind a router/NAT."
|
||||
echo "To make https://skyarts.ddns.net accessible:"
|
||||
echo ""
|
||||
echo "1. LOG INTO YOUR ROUTER"
|
||||
echo " IP: Check your router's IP (usually 192.168.10.1)"
|
||||
echo ""
|
||||
echo "2. SET UP PORT FORWARDING:"
|
||||
echo " External Port: 443"
|
||||
echo " Internal IP: 192.168.10.130"
|
||||
echo " Internal Port: 443"
|
||||
echo " Protocol: TCP"
|
||||
echo ""
|
||||
echo "3. ALSO FORWARD (if not already done):"
|
||||
echo " External Port: 80"
|
||||
echo " Internal IP: 192.168.10.130"
|
||||
echo " Internal Port: 80"
|
||||
echo " Protocol: TCP"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " TEST AFTER PORT FORWARDING"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Once port forwarding is configured:"
|
||||
echo ""
|
||||
echo "1. From your browser:"
|
||||
echo " https://skyarts.ddns.net"
|
||||
echo " https://skyarts.ddns.net/admin/login"
|
||||
echo ""
|
||||
echo "2. Login credentials:"
|
||||
echo " Email: admin@example.com"
|
||||
echo " Password: Admin123"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
@@ -1,28 +1,34 @@
|
||||
const requireAuth = (req, res, next) => {
|
||||
if (req.session && req.session.adminId) {
|
||||
if (req.session && req.session.user && req.session.user.id) {
|
||||
return next();
|
||||
}
|
||||
res.redirect('/admin/login');
|
||||
res.status(401).json({ success: false, message: "Authentication required" });
|
||||
};
|
||||
|
||||
const requireRole = (allowedRoles) => {
|
||||
// Allow single role or array of roles
|
||||
const roles = Array.isArray(allowedRoles) ? allowedRoles : [allowedRoles];
|
||||
|
||||
return (req, res, next) => {
|
||||
if (!req.session || !req.session.adminId) {
|
||||
return res.redirect('/admin/login');
|
||||
if (!req.session || !req.session.user || !req.session.user.id) {
|
||||
return res
|
||||
.status(401)
|
||||
.json({ success: false, message: "Authentication required" });
|
||||
}
|
||||
const userRole = req.session.role || 'user';
|
||||
if (allowedRoles.includes(userRole)) {
|
||||
|
||||
const userRole = req.session.user.role_id || "role-admin";
|
||||
|
||||
if (roles.includes(userRole)) {
|
||||
return next();
|
||||
}
|
||||
res.status(403).send('Access denied');
|
||||
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "Access denied. Insufficient permissions.",
|
||||
required_role: roles,
|
||||
your_role: userRole,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const redirectIfAuth = (req, res, next) => {
|
||||
if (req.session && req.session.adminId) {
|
||||
return res.redirect('/admin/dashboard');
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = { requireAuth, requireRole, redirectIfAuth };
|
||||
module.exports = { requireAuth, requireRole };
|
||||
|
||||
32
backend/quick-setup.sql
Normal file
32
backend/quick-setup.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- Quick setup script for SkyArtShop backend
|
||||
-- Run with: psql -U skyartapp -d skyartshop -f quick-setup.sql
|
||||
|
||||
\echo 'Creating adminusers table...'
|
||||
CREATE TABLE IF NOT EXISTS adminusers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
passwordhash TEXT NOT NULL,
|
||||
role VARCHAR(50) DEFAULT 'admin',
|
||||
createdat TIMESTAMP DEFAULT NOW(),
|
||||
lastlogin TIMESTAMP
|
||||
);
|
||||
|
||||
\echo 'Creating temporary admin user...'
|
||||
-- Email: admin@skyartshop.com
|
||||
-- Password: Admin123!
|
||||
DELETE FROM adminusers WHERE email = 'admin@skyartshop.com';
|
||||
INSERT INTO adminusers (email, name, passwordhash, role) VALUES
|
||||
('admin@skyartshop.com', 'Admin User', '$2b$10$vN9gE1VTxH3qH3qH3qH3qOqXZ5J8YqH3qH3qH3qH3qH3qH3qH3qH3u', 'superadmin');
|
||||
|
||||
\echo 'Verifying admin user...'
|
||||
SELECT id, email, name, role, createdat FROM adminusers;
|
||||
|
||||
\echo ''
|
||||
\echo '========================================='
|
||||
\echo 'Setup Complete!'
|
||||
\echo '========================================='
|
||||
\echo 'Login credentials:'
|
||||
\echo ' Email: admin@skyartshop.com'
|
||||
\echo ' Password: Admin123!'
|
||||
\echo '========================================='
|
||||
@@ -1,79 +1,97 @@
|
||||
const express = require('express');
|
||||
const { query } = require('../config/database');
|
||||
const { requireAuth } = require('../middleware/auth');
|
||||
const express = require("express");
|
||||
const { query } = require("../config/database");
|
||||
const { requireAuth } = require("../middleware/auth");
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/dashboard', requireAuth, async (req, res) => {
|
||||
// Dashboard stats API
|
||||
router.get("/dashboard/stats", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const productsCount = await query('SELECT COUNT(*) FROM products');
|
||||
const ordersCount = await query('SELECT COUNT(*) FROM orders');
|
||||
const usersCount = await query('SELECT COUNT(*) FROM appusers');
|
||||
const pagesCount = await query('SELECT COUNT(*) FROM pages');
|
||||
const recentOrders = await query(
|
||||
'SELECT id, ordernumber, totalamount, status, createdat FROM orders ORDER BY createdat DESC LIMIT 5'
|
||||
);
|
||||
res.render('admin/dashboard', {
|
||||
title: 'Dashboard - SkyArtShop Admin',
|
||||
user: req.session,
|
||||
const productsCount = await query("SELECT COUNT(*) FROM products");
|
||||
const projectsCount = await query("SELECT COUNT(*) FROM portfolioprojects");
|
||||
const blogCount = await query("SELECT COUNT(*) FROM blogposts");
|
||||
const pagesCount = await query("SELECT COUNT(*) FROM pages");
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
stats: {
|
||||
products: productsCount.rows[0].count,
|
||||
orders: ordersCount.rows[0].count,
|
||||
users: usersCount.rows[0].count,
|
||||
pages: pagesCount.rows[0].count
|
||||
products: parseInt(productsCount.rows[0].count),
|
||||
projects: parseInt(projectsCount.rows[0].count),
|
||||
blog: parseInt(blogCount.rows[0].count),
|
||||
pages: parseInt(pagesCount.rows[0].count),
|
||||
},
|
||||
user: {
|
||||
name: req.session.name,
|
||||
email: req.session.email,
|
||||
role: req.session.role,
|
||||
},
|
||||
recentOrders: recentOrders.rows
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Dashboard error:', error);
|
||||
res.status(500).send('Server error');
|
||||
console.error("Dashboard error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/products', requireAuth, async (req, res) => {
|
||||
// Products API
|
||||
router.get("/products", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const result = await query(
|
||||
'SELECT id, name, price, stockquantity, isactive, createdat FROM products ORDER BY createdat DESC'
|
||||
"SELECT id, name, price, stockquantity, isactive, createdat FROM products ORDER BY createdat DESC"
|
||||
);
|
||||
res.render('admin/products', {
|
||||
title: 'Products - SkyArtShop Admin',
|
||||
user: req.session,
|
||||
products: result.rows
|
||||
res.json({
|
||||
success: true,
|
||||
products: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Products error:', error);
|
||||
res.status(500).send('Server error');
|
||||
console.error("Products error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/orders', requireAuth, async (req, res) => {
|
||||
// Portfolio Projects API
|
||||
router.get("/portfolio/projects", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const result = await query(
|
||||
'SELECT id, ordernumber, totalamount, status, createdat FROM orders ORDER BY createdat DESC'
|
||||
"SELECT id, title, description, imageurl, categoryid, createdat FROM portfolioprojects ORDER BY createdat DESC"
|
||||
);
|
||||
res.render('admin/orders', {
|
||||
title: 'Orders - SkyArtShop Admin',
|
||||
user: req.session,
|
||||
orders: result.rows
|
||||
res.json({
|
||||
success: true,
|
||||
projects: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Orders error:', error);
|
||||
res.status(500).send('Server error');
|
||||
console.error("Portfolio error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/users', requireAuth, async (req, res) => {
|
||||
// Blog Posts API
|
||||
router.get("/blog", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const result = await query(
|
||||
'SELECT id, email, name, role, createdat, lastlogin FROM adminusers ORDER BY createdat DESC'
|
||||
"SELECT id, title, slug, excerpt, ispublished, createdat FROM blogposts ORDER BY createdat DESC"
|
||||
);
|
||||
res.render('admin/users', {
|
||||
title: 'Admin Users - SkyArtShop Admin',
|
||||
user: req.session,
|
||||
users: result.rows
|
||||
res.json({
|
||||
success: true,
|
||||
posts: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Users error:', error);
|
||||
res.status(500).send('Server error');
|
||||
console.error("Blog error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Pages API
|
||||
router.get("/pages", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const result = await query(
|
||||
"SELECT id, title, slug, ispublished, createdat FROM pages ORDER BY createdat DESC"
|
||||
);
|
||||
res.json({
|
||||
success: true,
|
||||
pages: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Pages error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,49 +1,100 @@
|
||||
const express = require("express");
|
||||
const bcrypt = require("bcrypt");
|
||||
const { query } = require("../config/database");
|
||||
const { redirectIfAuth } = require("../middleware/auth");
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/login", redirectIfAuth, (req, res) => {
|
||||
res.render("admin/login", {
|
||||
error: req.query.error,
|
||||
title: "Admin Login - SkyArtShop",
|
||||
});
|
||||
});
|
||||
|
||||
// Login endpoint (JSON API)
|
||||
router.post("/login", async (req, res) => {
|
||||
const { email, password } = req.body;
|
||||
try {
|
||||
const result = await query(
|
||||
"SELECT id, email, name, passwordhash, role FROM adminusers WHERE email = $1",
|
||||
`
|
||||
SELECT u.id, u.email, u.username, u.passwordhash, u.role_id, u.isactive,
|
||||
r.name as role_name, r.permissions
|
||||
FROM adminusers u
|
||||
LEFT JOIN roles r ON u.role_id = r.id
|
||||
WHERE u.email = $1
|
||||
`,
|
||||
[email]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.redirect("/admin/login?error=invalid");
|
||||
return res
|
||||
.status(401)
|
||||
.json({ success: false, message: "Invalid email or password" });
|
||||
}
|
||||
|
||||
const admin = result.rows[0];
|
||||
|
||||
// Check if user is active
|
||||
if (!admin.isactive) {
|
||||
return res
|
||||
.status(401)
|
||||
.json({ success: false, message: "Account is deactivated" });
|
||||
}
|
||||
|
||||
const validPassword = await bcrypt.compare(password, admin.passwordhash);
|
||||
if (!validPassword) {
|
||||
return res.redirect("/admin/login?error=invalid");
|
||||
return res
|
||||
.status(401)
|
||||
.json({ success: false, message: "Invalid email or password" });
|
||||
}
|
||||
await query("UPDATE adminusers SET lastlogin = NOW() WHERE id = $1", [
|
||||
|
||||
// Update last login
|
||||
await query("UPDATE adminusers SET last_login = NOW() WHERE id = $1", [
|
||||
admin.id,
|
||||
]);
|
||||
req.session.adminId = admin.id;
|
||||
req.session.email = admin.email;
|
||||
req.session.name = admin.name;
|
||||
req.session.role = admin.role;
|
||||
res.redirect("/admin/dashboard");
|
||||
|
||||
// Store user info in session
|
||||
req.session.user = {
|
||||
id: admin.id,
|
||||
email: admin.email,
|
||||
username: admin.username,
|
||||
role_id: admin.role_id,
|
||||
role_name: admin.role_name,
|
||||
permissions: admin.permissions,
|
||||
};
|
||||
|
||||
// Save session before responding
|
||||
req.session.save((err) => {
|
||||
if (err) {
|
||||
console.error("Session save error:", err);
|
||||
return res
|
||||
.status(500)
|
||||
.json({ success: false, message: "Session error" });
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
user: req.session.user,
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Login error:", error);
|
||||
res.redirect("/admin/login?error=server");
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/logout", (req, res) => {
|
||||
// Check session endpoint
|
||||
router.get("/session", (req, res) => {
|
||||
if (req.session && req.session.user) {
|
||||
res.json({
|
||||
authenticated: true,
|
||||
user: req.session.user,
|
||||
});
|
||||
} else {
|
||||
res.status(401).json({ authenticated: false });
|
||||
}
|
||||
});
|
||||
|
||||
// Logout endpoint
|
||||
router.post("/logout", (req, res) => {
|
||||
req.session.destroy((err) => {
|
||||
if (err) console.error("Logout error:", err);
|
||||
res.redirect("/admin/login");
|
||||
if (err) {
|
||||
console.error("Logout error:", err);
|
||||
return res.status(500).json({ success: false, message: "Logout failed" });
|
||||
}
|
||||
res.json({ success: true, message: "Logged out successfully" });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,38 +1,122 @@
|
||||
const express = require('express');
|
||||
const { query } = require('../config/database');
|
||||
const express = require("express");
|
||||
const { query } = require("../config/database");
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/', async (req, res) => {
|
||||
// Get all products
|
||||
router.get("/products", async (req, res) => {
|
||||
try {
|
||||
const products = await query(
|
||||
'SELECT id, name, description, price, imageurl FROM products WHERE isactive = true ORDER BY createdat DESC LIMIT 8'
|
||||
const result = await query(
|
||||
"SELECT id, name, description, shortdescription, price, imageurl, images, category, color, stockquantity, isactive, createdat FROM products WHERE isactive = true ORDER BY createdat DESC"
|
||||
);
|
||||
const sections = await query(
|
||||
'SELECT * FROM homepagesections ORDER BY displayorder ASC'
|
||||
);
|
||||
res.render('public/home', {
|
||||
title: 'Welcome - SkyArtShop',
|
||||
products: products.rows,
|
||||
sections: sections.rows
|
||||
res.json({
|
||||
success: true,
|
||||
products: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Home page error:', error);
|
||||
res.status(500).send('Server error');
|
||||
console.error("Products API error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/shop', async (req, res) => {
|
||||
// Get featured products
|
||||
router.get("/products/featured", async (req, res) => {
|
||||
try {
|
||||
const products = await query(
|
||||
'SELECT id, name, description, price, imageurl, category FROM products WHERE isactive = true ORDER BY name ASC'
|
||||
const limit = parseInt(req.query.limit) || 4;
|
||||
const result = await query(
|
||||
"SELECT id, name, description, price, imageurl, images FROM products WHERE isactive = true ORDER BY createdat DESC LIMIT $1",
|
||||
[limit]
|
||||
);
|
||||
res.render('public/shop', {
|
||||
title: 'Shop - SkyArtShop',
|
||||
products: products.rows
|
||||
res.json({
|
||||
success: true,
|
||||
products: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Shop page error:', error);
|
||||
res.status(500).send('Server error');
|
||||
console.error("Featured products error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Get single product
|
||||
router.get("/products/:id", async (req, res) => {
|
||||
try {
|
||||
const result = await query(
|
||||
"SELECT * FROM products WHERE id = $1 AND isactive = true",
|
||||
[req.params.id]
|
||||
);
|
||||
if (result.rows.length === 0) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ success: false, message: "Product not found" });
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
product: result.rows[0],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Product detail error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Get site settings
|
||||
router.get("/settings", async (req, res) => {
|
||||
try {
|
||||
const result = await query("SELECT * FROM sitesettings LIMIT 1");
|
||||
res.json({
|
||||
success: true,
|
||||
settings: result.rows[0] || {},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Settings error:", error);
|
||||
res.json({ success: true, settings: {} });
|
||||
}
|
||||
});
|
||||
|
||||
// Get homepage sections
|
||||
router.get("/homepage/sections", async (req, res) => {
|
||||
try {
|
||||
const result = await query(
|
||||
"SELECT * FROM homepagesections ORDER BY displayorder ASC"
|
||||
);
|
||||
res.json({
|
||||
success: true,
|
||||
sections: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Homepage sections error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Get portfolio projects
|
||||
router.get("/portfolio/projects", async (req, res) => {
|
||||
try {
|
||||
const result = await query(
|
||||
"SELECT id, title, description, imageurl, categoryid, createdat FROM portfolioprojects ORDER BY createdat DESC"
|
||||
);
|
||||
res.json({
|
||||
success: true,
|
||||
projects: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Portfolio error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Get blog posts
|
||||
router.get("/blog/posts", async (req, res) => {
|
||||
try {
|
||||
const result = await query(
|
||||
"SELECT id, title, slug, excerpt, content, imageurl, ispublished, createdat FROM blogposts WHERE ispublished = true ORDER BY createdat DESC"
|
||||
);
|
||||
res.json({
|
||||
success: true,
|
||||
posts: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Blog posts error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
334
backend/routes/users.js
Normal file
334
backend/routes/users.js
Normal file
@@ -0,0 +1,334 @@
|
||||
const express = require("express");
|
||||
const bcrypt = require("bcrypt");
|
||||
const { query } = require("../config/database");
|
||||
const { requireAuth, requireRole } = require("../middleware/auth");
|
||||
const router = express.Router();
|
||||
|
||||
// Require admin role for all routes
|
||||
router.use(requireAuth);
|
||||
router.use(requireRole("role-admin"));
|
||||
|
||||
// Get all users with roles
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
const result = await query(`
|
||||
SELECT
|
||||
u.id, u.username, u.email, u.role_id, u.isactive,
|
||||
u.last_login, u.createdat, u.password_never_expires,
|
||||
u.password_expires_at, u.last_password_change,
|
||||
r.name as role_name, r.description as role_description
|
||||
FROM adminusers u
|
||||
LEFT JOIN roles r ON u.role_id = r.id
|
||||
ORDER BY u.createdat DESC
|
||||
`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
users: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Get users error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Get all roles
|
||||
router.get("/roles", async (req, res) => {
|
||||
try {
|
||||
const result = await query(`
|
||||
SELECT id, name, description, permissions
|
||||
FROM roles
|
||||
ORDER BY name
|
||||
`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
roles: result.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Get roles error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Create new user
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const { username, email, password, role_id, password_never_expires } =
|
||||
req.body;
|
||||
|
||||
// Validate required fields
|
||||
if (!username || !email || !password || !role_id) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Username, email, password, and role are required",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user already exists
|
||||
const existing = await query("SELECT id FROM adminusers WHERE email = $1", [
|
||||
email,
|
||||
]);
|
||||
|
||||
if (existing.rows.length > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "User with this email already exists",
|
||||
});
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
// Calculate password expiry (90 days from now if not never expires)
|
||||
let passwordExpiresAt = null;
|
||||
if (!password_never_expires) {
|
||||
const expiryDate = new Date();
|
||||
expiryDate.setDate(expiryDate.getDate() + 90);
|
||||
passwordExpiresAt = expiryDate.toISOString();
|
||||
}
|
||||
|
||||
// Insert new user
|
||||
const result = await query(
|
||||
`
|
||||
INSERT INTO adminusers (
|
||||
id, username, email, passwordhash, role_id,
|
||||
password_never_expires, password_expires_at,
|
||||
isactive, created_by, createdat, last_password_change
|
||||
) VALUES (
|
||||
'user-' || gen_random_uuid()::text,
|
||||
$1, $2, $3, $4, $5, $6, true, $7, NOW(), NOW()
|
||||
)
|
||||
RETURNING id, username, email, role_id, isactive, createdat
|
||||
`,
|
||||
[
|
||||
username,
|
||||
email,
|
||||
hashedPassword,
|
||||
role_id,
|
||||
password_never_expires || false,
|
||||
passwordExpiresAt,
|
||||
req.session.user.email,
|
||||
]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "User created successfully",
|
||||
user: result.rows[0],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Create user error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Update user
|
||||
router.put("/:id", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { username, email, role_id, isactive, password_never_expires } =
|
||||
req.body;
|
||||
|
||||
// Build update query dynamically
|
||||
const updates = [];
|
||||
const values = [];
|
||||
let paramCount = 1;
|
||||
|
||||
if (username !== undefined) {
|
||||
updates.push(`username = $${paramCount++}`);
|
||||
values.push(username);
|
||||
}
|
||||
if (email !== undefined) {
|
||||
updates.push(`email = $${paramCount++}`);
|
||||
values.push(email);
|
||||
}
|
||||
if (role_id !== undefined) {
|
||||
updates.push(`role_id = $${paramCount++}`);
|
||||
values.push(role_id);
|
||||
}
|
||||
if (isactive !== undefined) {
|
||||
updates.push(`isactive = $${paramCount++}`);
|
||||
values.push(isactive);
|
||||
}
|
||||
if (password_never_expires !== undefined) {
|
||||
updates.push(`password_never_expires = $${paramCount++}`);
|
||||
values.push(password_never_expires);
|
||||
|
||||
// If setting to never expire, clear expiry date
|
||||
if (password_never_expires) {
|
||||
updates.push(`password_expires_at = NULL`);
|
||||
}
|
||||
}
|
||||
|
||||
updates.push(`updated_at = NOW()`);
|
||||
values.push(id);
|
||||
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE adminusers
|
||||
SET ${updates.join(", ")}
|
||||
WHERE id = $${paramCount}
|
||||
RETURNING id, username, email, role_id, isactive, password_never_expires
|
||||
`,
|
||||
values
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "User updated successfully",
|
||||
user: result.rows[0],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Update user error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Reset user password
|
||||
router.post("/:id/reset-password", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { new_password } = req.body;
|
||||
|
||||
if (!new_password || new_password.length < 6) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Password must be at least 6 characters long",
|
||||
});
|
||||
}
|
||||
|
||||
// Hash new password
|
||||
const hashedPassword = await bcrypt.hash(new_password, 10);
|
||||
|
||||
// Get user's password expiry setting
|
||||
const userResult = await query(
|
||||
"SELECT password_never_expires FROM adminusers WHERE id = $1",
|
||||
[id]
|
||||
);
|
||||
|
||||
if (userResult.rows.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate new expiry date (90 days from now if not never expires)
|
||||
let passwordExpiresAt = null;
|
||||
if (!userResult.rows[0].password_never_expires) {
|
||||
const expiryDate = new Date();
|
||||
expiryDate.setDate(expiryDate.getDate() + 90);
|
||||
passwordExpiresAt = expiryDate.toISOString();
|
||||
}
|
||||
|
||||
// Update password
|
||||
await query(
|
||||
`
|
||||
UPDATE adminusers
|
||||
SET passwordhash = $1,
|
||||
password_expires_at = $2,
|
||||
last_password_change = NOW(),
|
||||
updated_at = NOW()
|
||||
WHERE id = $3
|
||||
`,
|
||||
[hashedPassword, passwordExpiresAt, id]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Password reset successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Reset password error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete user
|
||||
router.delete("/:id", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Prevent deleting yourself
|
||||
if (id === req.session.user.id) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Cannot delete your own account",
|
||||
});
|
||||
}
|
||||
|
||||
const result = await query(
|
||||
"DELETE FROM adminusers WHERE id = $1 RETURNING id",
|
||||
[id]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "User deleted successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Delete user error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle user active status
|
||||
router.post("/:id/toggle-status", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Prevent deactivating yourself
|
||||
if (id === req.session.user.id) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Cannot deactivate your own account",
|
||||
});
|
||||
}
|
||||
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE adminusers
|
||||
SET isactive = NOT isactive,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING id, isactive
|
||||
`,
|
||||
[id]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `User ${
|
||||
result.rows[0].isactive ? "activated" : "deactivated"
|
||||
} successfully`,
|
||||
isactive: result.rows[0].isactive,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Toggle status error:", error);
|
||||
res.status(500).json({ success: false, message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,36 +1,42 @@
|
||||
const express = require('express');
|
||||
const session = require('express-session');
|
||||
const pgSession = require('connect-pg-simple')(session);
|
||||
const path = require('path');
|
||||
const { pool } = require('./config/database');
|
||||
require('dotenv').config();
|
||||
const express = require("express");
|
||||
const session = require("express-session");
|
||||
const pgSession = require("connect-pg-simple")(session);
|
||||
const path = require("path");
|
||||
const { pool } = require("./config/database");
|
||||
require("dotenv").config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
const PORT = process.env.PORT || 5000;
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
// Serve static files from /var/www/skyartshop
|
||||
app.use(express.static("/var/www/skyartshop/public"));
|
||||
app.use("/assets", express.static("/var/www/skyartshop/assets"));
|
||||
app.use("/uploads", express.static("/var/www/skyartshop/uploads"));
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use('/assets', express.static(path.join(__dirname, '../wwwroot/assets')));
|
||||
app.use('/uploads', express.static(path.join(__dirname, '../wwwroot/uploads')));
|
||||
|
||||
app.use(session({
|
||||
store: new pgSession({
|
||||
pool: pool,
|
||||
tableName: 'session',
|
||||
createTableIfMissing: true
|
||||
}),
|
||||
secret: process.env.SESSION_SECRET || 'skyart-shop-secret-2025',
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: false,
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000
|
||||
}
|
||||
}));
|
||||
app.use(
|
||||
session({
|
||||
store: new pgSession({
|
||||
pool: pool,
|
||||
tableName: "session",
|
||||
createTableIfMissing: true,
|
||||
}),
|
||||
secret: process.env.SESSION_SECRET || "skyart-shop-secret-2025",
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: process.env.NODE_ENV === "production" ? true : false,
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000,
|
||||
sameSite: "lax",
|
||||
domain: process.env.NODE_ENV === "production" ? ".ddns.net" : "localhost",
|
||||
},
|
||||
proxy: true,
|
||||
name: "skyartshop.sid",
|
||||
})
|
||||
);
|
||||
|
||||
app.use((req, res, next) => {
|
||||
res.locals.session = req.session;
|
||||
@@ -38,47 +44,66 @@ app.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
const authRoutes = require('./routes/auth');
|
||||
const adminRoutes = require('./routes/admin');
|
||||
const publicRoutes = require('./routes/public');
|
||||
// API Routes
|
||||
const authRoutes = require("./routes/auth");
|
||||
const adminRoutes = require("./routes/admin");
|
||||
const publicRoutes = require("./routes/public");
|
||||
const usersRoutes = require("./routes/users");
|
||||
|
||||
app.use('/admin', authRoutes);
|
||||
app.use('/admin', adminRoutes);
|
||||
app.use('/', publicRoutes);
|
||||
// Admin redirect - handle /admin to redirect to login (must be before static files)
|
||||
app.get("/admin", (req, res) => {
|
||||
res.redirect("/admin/login.html");
|
||||
});
|
||||
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
app.get("/admin/", (req, res) => {
|
||||
res.redirect("/admin/login.html");
|
||||
});
|
||||
|
||||
// API Routes
|
||||
app.use("/api/admin", authRoutes);
|
||||
app.use("/api/admin", adminRoutes);
|
||||
app.use("/api/admin/users", usersRoutes);
|
||||
app.use("/api", publicRoutes);
|
||||
|
||||
// Admin static files (must be after redirect routes)
|
||||
app.use("/admin", express.static("/var/www/skyartshop/admin"));
|
||||
|
||||
// Root redirect to admin login
|
||||
app.get("/", (req, res) => {
|
||||
res.redirect("/admin/login.html");
|
||||
});
|
||||
|
||||
app.get("/health", (req, res) => {
|
||||
res.json({
|
||||
status: "ok",
|
||||
timestamp: new Date().toISOString(),
|
||||
database: 'connected'
|
||||
database: "connected",
|
||||
});
|
||||
});
|
||||
|
||||
app.use((req, res) => {
|
||||
res.status(404).render('public/404', {
|
||||
title: '404 - Page Not Found'
|
||||
});
|
||||
res.status(404).json({ error: "Not found" });
|
||||
});
|
||||
|
||||
app.use((err, req, res, next) => {
|
||||
console.error('Error:', err);
|
||||
res.status(500).send('Server error');
|
||||
console.error("Error:", err);
|
||||
res.status(500).json({ error: "Server error" });
|
||||
});
|
||||
|
||||
app.listen(PORT, '0.0.0.0', () => {
|
||||
console.log('========================================');
|
||||
console.log(' SkyArtShop Backend Server');
|
||||
console.log('========================================');
|
||||
app.listen(PORT, "0.0.0.0", () => {
|
||||
console.log("========================================");
|
||||
console.log(" SkyArtShop Backend Server");
|
||||
console.log("========================================");
|
||||
console.log(`🚀 Server running on http://localhost:${PORT}`);
|
||||
console.log(`📦 Environment: ${process.env.NODE_ENV || 'development'}`);
|
||||
console.log(`📦 Environment: ${process.env.NODE_ENV || "development"}`);
|
||||
console.log(`🗄️ Database: PostgreSQL (${process.env.DB_NAME})`);
|
||||
console.log('========================================');
|
||||
console.log("========================================");
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
console.log('SIGTERM received, closing server...');
|
||||
process.on("SIGTERM", () => {
|
||||
console.log("SIGTERM received, closing server...");
|
||||
pool.end(() => {
|
||||
console.log('Database pool closed');
|
||||
console.log("Database pool closed");
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
48
backend/setup-database.sql
Normal file
48
backend/setup-database.sql
Normal file
@@ -0,0 +1,48 @@
|
||||
-- Create adminusers table if it doesn't exist
|
||||
CREATE TABLE IF NOT EXISTS adminusers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
passwordhash TEXT NOT NULL,
|
||||
role VARCHAR(50) DEFAULT 'admin',
|
||||
createdat TIMESTAMP DEFAULT NOW(),
|
||||
lastlogin TIMESTAMP
|
||||
);
|
||||
|
||||
-- Insert temporary admin user
|
||||
-- Password: TempAdmin2024!
|
||||
-- Bcrypt hash generated with 10 salt rounds
|
||||
INSERT INTO adminusers (email, name, passwordhash, role, createdat)
|
||||
VALUES (
|
||||
'admin@skyartshop.com',
|
||||
'Temporary Admin',
|
||||
'$2b$10$YvK5rQE4nHjZH5tVFZ1lNu5iK7Jx/lMQXZvhGEg8sK1vF0N3wL5oG',
|
||||
'superadmin',
|
||||
NOW()
|
||||
)
|
||||
ON CONFLICT (email) DO UPDATE
|
||||
SET passwordhash = EXCLUDED.passwordhash,
|
||||
name = EXCLUDED.name,
|
||||
role = EXCLUDED.role;
|
||||
|
||||
-- Create appusers table for public users (if needed)
|
||||
CREATE TABLE IF NOT EXISTS appusers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
passwordhash TEXT NOT NULL,
|
||||
createdat TIMESTAMP DEFAULT NOW(),
|
||||
lastlogin TIMESTAMP
|
||||
);
|
||||
|
||||
-- Create sessions table for express-session
|
||||
CREATE TABLE IF NOT EXISTS session (
|
||||
sid VARCHAR NOT NULL COLLATE "default",
|
||||
sess JSON NOT NULL,
|
||||
expire TIMESTAMP(6) NOT NULL,
|
||||
PRIMARY KEY (sid)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS IDX_session_expire ON session (expire);
|
||||
|
||||
SELECT 'Database setup complete!' as status;
|
||||
46
backend/setup-user-roles.sql
Normal file
46
backend/setup-user-roles.sql
Normal file
@@ -0,0 +1,46 @@
|
||||
-- Create roles table
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
id VARCHAR(50) PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
permissions JSONB DEFAULT '{}',
|
||||
createdat TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Insert default roles
|
||||
INSERT INTO roles (id, name, description, permissions) VALUES
|
||||
('role-admin', 'Admin', 'Full system access and management', '{"manage_users": true, "manage_products": true, "manage_orders": true, "manage_content": true, "view_reports": true, "manage_settings": true}'),
|
||||
('role-accountant', 'Accountant', 'Financial and reporting access', '{"view_orders": true, "view_reports": true, "manage_products": false, "manage_users": false}'),
|
||||
('role-sales', 'Sales', 'Product and order management', '{"manage_products": true, "manage_orders": true, "view_reports": true, "manage_users": false}'),
|
||||
('role-cashier', 'Cashier', 'Basic order processing', '{"process_orders": true, "view_products": true, "manage_products": false, "manage_users": false}')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Update adminusers table to add role and password expiry fields
|
||||
ALTER TABLE adminusers
|
||||
ADD COLUMN IF NOT EXISTS role_id VARCHAR(50) DEFAULT 'role-admin',
|
||||
ADD COLUMN IF NOT EXISTS password_expires_at TIMESTAMP,
|
||||
ADD COLUMN IF NOT EXISTS password_never_expires BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS last_password_change TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
ADD COLUMN IF NOT EXISTS isactive BOOLEAN DEFAULT true,
|
||||
ADD COLUMN IF NOT EXISTS last_login TIMESTAMP,
|
||||
ADD COLUMN IF NOT EXISTS created_by VARCHAR(255),
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
-- Add foreign key constraint
|
||||
ALTER TABLE adminusers
|
||||
ADD CONSTRAINT fk_role
|
||||
FOREIGN KEY (role_id) REFERENCES roles(id)
|
||||
ON DELETE SET NULL;
|
||||
|
||||
-- Update existing admin user
|
||||
UPDATE adminusers
|
||||
SET role_id = 'role-admin',
|
||||
password_never_expires = true,
|
||||
isactive = true
|
||||
WHERE email = 'admin@example.com';
|
||||
|
||||
-- Create index for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_adminusers_role ON adminusers(role_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_adminusers_email ON adminusers(email);
|
||||
|
||||
SELECT 'User roles setup complete' as status;
|
||||
52
backend/test-login.js
Normal file
52
backend/test-login.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const express = require("express");
|
||||
const bcrypt = require("bcrypt");
|
||||
const { query } = require("./config/database");
|
||||
|
||||
async function testLogin() {
|
||||
const email = "admin@example.com";
|
||||
const password = "Admin123";
|
||||
|
||||
try {
|
||||
console.log("1. Querying database for user...");
|
||||
const result = await query(
|
||||
"SELECT id, email, name, passwordhash, role FROM adminusers WHERE email = $1",
|
||||
[email]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
console.log("❌ User not found");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("2. User found:", result.rows[0].email);
|
||||
|
||||
const admin = result.rows[0];
|
||||
console.log("3. Comparing password...");
|
||||
const validPassword = await bcrypt.compare(password, admin.passwordhash);
|
||||
|
||||
if (!validPassword) {
|
||||
console.log("❌ Invalid password");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("4. ✓ Password valid!");
|
||||
|
||||
console.log("5. Updating last login...");
|
||||
await query("UPDATE adminusers SET lastlogin = NOW() WHERE id = $1", [
|
||||
admin.id,
|
||||
]);
|
||||
|
||||
console.log("6. ✓ Login successful!");
|
||||
console.log(" User ID:", admin.id);
|
||||
console.log(" Email:", admin.email);
|
||||
console.log(" Name:", admin.name);
|
||||
console.log(" Role:", admin.role);
|
||||
} catch (error) {
|
||||
console.error("❌ Error during login:", error.message);
|
||||
console.error(" Stack:", error.stack);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testLogin();
|
||||
Reference in New Issue
Block a user