Move authentication to postgres
This commit is contained in:
@@ -1,5 +1,209 @@
|
|||||||
|
// ecosystem.config.js
|
||||||
|
const path = require('path');
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
|
||||||
|
// Load environment variables safely with error handling
|
||||||
|
const loadEnvFile = (envPath) => {
|
||||||
|
try {
|
||||||
|
console.log('Loading env from:', envPath);
|
||||||
|
const result = dotenv.config({ path: envPath });
|
||||||
|
if (result.error) {
|
||||||
|
console.warn(`Warning: .env file not found or invalid at ${envPath}:`, result.error.message);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
console.log('Env variables loaded from', envPath, ':', Object.keys(result.parsed || {}));
|
||||||
|
return result.parsed || {};
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Warning: Error loading .env file at ${envPath}:`, error.message);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load environment variables for each server
|
||||||
|
const authEnv = loadEnvFile(path.resolve(__dirname, 'dashboard/auth-server/.env'));
|
||||||
|
const aircallEnv = loadEnvFile(path.resolve(__dirname, 'dashboard/aircall-server/.env'));
|
||||||
|
const klaviyoEnv = loadEnvFile(path.resolve(__dirname, 'dashboard/klaviyo-server/.env'));
|
||||||
|
const metaEnv = loadEnvFile(path.resolve(__dirname, 'dashboard/meta-server/.env'));
|
||||||
|
const googleAnalyticsEnv = require('dotenv').config({
|
||||||
|
path: path.resolve(__dirname, 'dashboard/google-server/.env')
|
||||||
|
}).parsed || {};
|
||||||
|
const typeformEnv = loadEnvFile(path.resolve(__dirname, 'dashboard/typeform-server/.env'));
|
||||||
|
const inventoryEnv = loadEnvFile(path.resolve(__dirname, 'inventory/.env'));
|
||||||
|
|
||||||
|
// Common log settings for all apps
|
||||||
|
const logSettings = {
|
||||||
|
log_rotate: true,
|
||||||
|
max_size: '10M',
|
||||||
|
retain: '10',
|
||||||
|
log_date_format: 'YYYY-MM-DD HH:mm:ss'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Common app settings
|
||||||
|
const commonSettings = {
|
||||||
|
instances: 1,
|
||||||
|
exec_mode: 'fork',
|
||||||
|
autorestart: true,
|
||||||
|
watch: false,
|
||||||
|
max_memory_restart: '1G',
|
||||||
|
time: true,
|
||||||
|
...logSettings,
|
||||||
|
ignore_watch: [
|
||||||
|
'node_modules',
|
||||||
|
'logs',
|
||||||
|
'.git',
|
||||||
|
'*.log'
|
||||||
|
],
|
||||||
|
min_uptime: 5000,
|
||||||
|
max_restarts: 5,
|
||||||
|
restart_delay: 4000,
|
||||||
|
listen_timeout: 50000,
|
||||||
|
kill_timeout: 5000,
|
||||||
|
node_args: '--max-old-space-size=1536'
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
apps: [
|
apps: [
|
||||||
|
{
|
||||||
|
...commonSettings,
|
||||||
|
name: 'auth-server',
|
||||||
|
script: './dashboard/auth-server/index.js',
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
PORT: 3003,
|
||||||
|
...authEnv
|
||||||
|
},
|
||||||
|
error_file: 'dashboard/auth-server/logs/pm2/err.log',
|
||||||
|
out_file: 'dashboard/auth-server/logs/pm2/out.log',
|
||||||
|
log_file: 'dashboard/auth-server/logs/pm2/combined.log',
|
||||||
|
env_production: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
PORT: 3003
|
||||||
|
},
|
||||||
|
env_development: {
|
||||||
|
NODE_ENV: 'development',
|
||||||
|
PORT: 3003
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...commonSettings,
|
||||||
|
name: 'aircall-server',
|
||||||
|
script: './dashboard/aircall-server/server.js',
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
AIRCALL_PORT: 3002,
|
||||||
|
...aircallEnv
|
||||||
|
},
|
||||||
|
error_file: 'dashboard/aircall-server/logs/pm2/err.log',
|
||||||
|
out_file: 'dashboard/aircall-server/logs/pm2/out.log',
|
||||||
|
log_file: 'dashboard/aircall-server/logs/pm2/combined.log',
|
||||||
|
env_production: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
AIRCALL_PORT: 3002
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...commonSettings,
|
||||||
|
name: 'klaviyo-server',
|
||||||
|
script: './dashboard/klaviyo-server/server.js',
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
KLAVIYO_PORT: 3004,
|
||||||
|
...klaviyoEnv
|
||||||
|
},
|
||||||
|
error_file: 'dashboard/klaviyo-server/logs/pm2/err.log',
|
||||||
|
out_file: 'dashboard/klaviyo-server/logs/pm2/out.log',
|
||||||
|
log_file: 'dashboard/klaviyo-server/logs/pm2/combined.log',
|
||||||
|
env_production: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
KLAVIYO_PORT: 3004
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...commonSettings,
|
||||||
|
name: 'meta-server',
|
||||||
|
script: './dashboard/meta-server/server.js',
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
PORT: 3005,
|
||||||
|
...metaEnv
|
||||||
|
},
|
||||||
|
error_file: 'dashboard/meta-server/logs/pm2/err.log',
|
||||||
|
out_file: 'dashboard/meta-server/logs/pm2/out.log',
|
||||||
|
log_file: 'dashboard/meta-server/logs/pm2/combined.log',
|
||||||
|
env_production: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
PORT: 3005
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gorgias-server",
|
||||||
|
script: "./dashboard/gorgias-server/server.js",
|
||||||
|
env: {
|
||||||
|
NODE_ENV: "development",
|
||||||
|
PORT: 3006
|
||||||
|
},
|
||||||
|
env_production: {
|
||||||
|
NODE_ENV: "production",
|
||||||
|
PORT: 3006
|
||||||
|
},
|
||||||
|
error_file: "dashboard/logs/gorgias-server-error.log",
|
||||||
|
out_file: "dashboard/logs/gorgias-server-out.log",
|
||||||
|
log_file: "dashboard/logs/gorgias-server-combined.log",
|
||||||
|
time: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...commonSettings,
|
||||||
|
name: 'google-server',
|
||||||
|
script: path.resolve(__dirname, 'dashboard/google-server/server.js'),
|
||||||
|
watch: false,
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
GOOGLE_ANALYTICS_PORT: 3007,
|
||||||
|
...googleAnalyticsEnv
|
||||||
|
},
|
||||||
|
error_file: path.resolve(__dirname, 'dashboard/google-server/logs/pm2/err.log'),
|
||||||
|
out_file: path.resolve(__dirname, 'dashboard/google-server/logs/pm2/out.log'),
|
||||||
|
log_file: path.resolve(__dirname, 'dashboard/google-server/logs/pm2/combined.log'),
|
||||||
|
env_production: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
GOOGLE_ANALYTICS_PORT: 3007
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...commonSettings,
|
||||||
|
name: 'typeform-server',
|
||||||
|
script: './dashboard/typeform-server/server.js',
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
TYPEFORM_PORT: 3008,
|
||||||
|
...typeformEnv
|
||||||
|
},
|
||||||
|
error_file: 'dashboard/typeform-server/logs/pm2/err.log',
|
||||||
|
out_file: 'dashboard/typeform-server/logs/pm2/out.log',
|
||||||
|
log_file: 'dashboard/typeform-server/logs/pm2/combined.log',
|
||||||
|
env_production: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
TYPEFORM_PORT: 3008
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...commonSettings,
|
||||||
|
name: 'inventory-server',
|
||||||
|
script: './inventory/src/server.js',
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
PORT: 3010,
|
||||||
|
...inventoryEnv
|
||||||
|
},
|
||||||
|
error_file: 'inventory/logs/pm2/err.log',
|
||||||
|
out_file: 'inventory/logs/pm2/out.log',
|
||||||
|
log_file: 'inventory/logs/pm2/combined.log',
|
||||||
|
env_production: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
PORT: 3010,
|
||||||
|
...inventoryEnv
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
...commonSettings,
|
...commonSettings,
|
||||||
name: 'new-auth-server',
|
name: 'new-auth-server',
|
||||||
@@ -7,16 +211,12 @@ module.exports = {
|
|||||||
env: {
|
env: {
|
||||||
NODE_ENV: 'production',
|
NODE_ENV: 'production',
|
||||||
AUTH_PORT: 3011,
|
AUTH_PORT: 3011,
|
||||||
|
...inventoryEnv,
|
||||||
JWT_SECRET: process.env.JWT_SECRET
|
JWT_SECRET: process.env.JWT_SECRET
|
||||||
},
|
},
|
||||||
error_file: 'inventory-server/auth/logs/pm2/err.log',
|
error_file: 'inventory-server/auth/logs/pm2/err.log',
|
||||||
out_file: 'inventory-server/auth/logs/pm2/out.log',
|
out_file: 'inventory-server/auth/logs/pm2/out.log',
|
||||||
log_file: 'inventory-server/auth/logs/pm2/combined.log',
|
log_file: 'inventory-server/auth/logs/pm2/combined.log'
|
||||||
env_production: {
|
|
||||||
NODE_ENV: 'production',
|
|
||||||
AUTH_PORT: 3011,
|
|
||||||
JWT_SECRET: process.env.JWT_SECRET
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
103
inventory-server/auth/add-user.js
Normal file
103
inventory-server/auth/add-user.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
require('dotenv').config({ path: '../.env' });
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
const { Pool } = require('pg');
|
||||||
|
const inquirer = require('inquirer');
|
||||||
|
|
||||||
|
// Log connection details for debugging (remove in production)
|
||||||
|
console.log('Attempting to connect with:', {
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
database: process.env.DB_NAME,
|
||||||
|
port: process.env.DB_PORT
|
||||||
|
});
|
||||||
|
|
||||||
|
const pool = new Pool({
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_NAME,
|
||||||
|
port: process.env.DB_PORT,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function promptUser() {
|
||||||
|
const questions = [
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
name: 'username',
|
||||||
|
message: 'Enter username:',
|
||||||
|
validate: (input) => {
|
||||||
|
if (input.length < 3) {
|
||||||
|
return 'Username must be at least 3 characters long';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'password',
|
||||||
|
name: 'password',
|
||||||
|
message: 'Enter password:',
|
||||||
|
mask: '*',
|
||||||
|
validate: (input) => {
|
||||||
|
if (input.length < 8) {
|
||||||
|
return 'Password must be at least 8 characters long';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'password',
|
||||||
|
name: 'confirmPassword',
|
||||||
|
message: 'Confirm password:',
|
||||||
|
mask: '*',
|
||||||
|
validate: (input, answers) => {
|
||||||
|
if (input !== answers.password) {
|
||||||
|
return 'Passwords do not match';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return inquirer.prompt(questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addUser() {
|
||||||
|
try {
|
||||||
|
// Get user input
|
||||||
|
const answers = await promptUser();
|
||||||
|
const { username, password } = answers;
|
||||||
|
|
||||||
|
// Hash password
|
||||||
|
const saltRounds = 10;
|
||||||
|
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
||||||
|
|
||||||
|
// Check if user already exists
|
||||||
|
const checkResult = await pool.query(
|
||||||
|
'SELECT id FROM users WHERE username = $1',
|
||||||
|
[username]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (checkResult.rows.length > 0) {
|
||||||
|
console.error('Error: Username already exists');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert new user
|
||||||
|
const result = await pool.query(
|
||||||
|
'INSERT INTO users (username, password) VALUES ($1, $2) RETURNING id',
|
||||||
|
[username, hashedPassword]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`User ${username} created successfully with id ${result.rows[0].id}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating user:', error);
|
||||||
|
console.error('Error details:', error.message);
|
||||||
|
if (error.code) {
|
||||||
|
console.error('Error code:', error.code);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await pool.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addUser();
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
const bcrypt = require('bcrypt');
|
|
||||||
const mysql = require('mysql2/promise');
|
|
||||||
const readline = require('readline').createInterface({
|
|
||||||
input: process.stdin,
|
|
||||||
output: process.stdout,
|
|
||||||
});
|
|
||||||
require('dotenv').config({ path: '../.env' });
|
|
||||||
|
|
||||||
const dbConfig = {
|
|
||||||
host: process.env.DB_HOST,
|
|
||||||
user: process.env.DB_USER,
|
|
||||||
password: process.env.DB_PASSWORD,
|
|
||||||
database: process.env.DB_NAME,
|
|
||||||
};
|
|
||||||
|
|
||||||
async function addUser() {
|
|
||||||
const username = await askQuestion('Enter username: ');
|
|
||||||
const password = await askQuestion('Enter password: ');
|
|
||||||
|
|
||||||
const hashedPassword = await bcrypt.hash(password, 10);
|
|
||||||
|
|
||||||
const connection = await mysql.createConnection(dbConfig);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await connection.query('INSERT INTO users (username, password) VALUES (?, ?)', [username, hashedPassword]);
|
|
||||||
console.log(`User ${username} added successfully.`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error adding user:', error);
|
|
||||||
} finally {
|
|
||||||
connection.end();
|
|
||||||
readline.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function askQuestion(query) {
|
|
||||||
return new Promise(resolve => readline.question(query, ans => {
|
|
||||||
resolve(ans);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
addUser();
|
|
||||||
880
inventory-server/auth/package-lock.json
generated
880
inventory-server/auth/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "auth-server",
|
"name": "inventory-auth-server",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Authentication server for inventory management",
|
"description": "Authentication server for inventory management system",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js",
|
"start": "node server.js"
|
||||||
"dev": "nodemon server.js",
|
|
||||||
"add_user": "node add_user.js"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.7",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^9.0.2"
|
"inquirer": "^8.2.6",
|
||||||
},
|
"jsonwebtoken": "^9.0.2",
|
||||||
"devDependencies": {
|
"morgan": "^1.10.0",
|
||||||
"nodemon": "^3.1.0"
|
"pg": "^8.11.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
CREATE TABLE `users` (
|
CREATE TABLE users (
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
`username` VARCHAR(255) NOT NULL UNIQUE,
|
username VARCHAR(255) NOT NULL UNIQUE,
|
||||||
`password` VARCHAR(255) NOT NULL,
|
password VARCHAR(255) NOT NULL,
|
||||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
@@ -1,135 +1,102 @@
|
|||||||
|
require('dotenv').config({ path: '../.env' });
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
const cors = require('cors');
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const cors = require('cors');
|
const { Pool } = require('pg');
|
||||||
const mysql = require('mysql2/promise');
|
const morgan = require('morgan');
|
||||||
require('dotenv').config({ path: '../.env' });
|
|
||||||
|
// Log startup configuration
|
||||||
|
console.log('Starting auth server with config:', {
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
database: process.env.DB_NAME,
|
||||||
|
port: process.env.DB_PORT,
|
||||||
|
auth_port: process.env.AUTH_PORT
|
||||||
|
});
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.AUTH_PORT || 3011;
|
const port = process.env.AUTH_PORT || 3011;
|
||||||
|
|
||||||
// Database configuration
|
// Database configuration
|
||||||
const dbConfig = {
|
const pool = new Pool({
|
||||||
host: process.env.DB_HOST,
|
host: process.env.DB_HOST,
|
||||||
user: process.env.DB_USER,
|
user: process.env.DB_USER,
|
||||||
password: process.env.DB_PASSWORD,
|
password: process.env.DB_PASSWORD,
|
||||||
database: process.env.DB_NAME,
|
database: process.env.DB_NAME,
|
||||||
};
|
port: process.env.DB_PORT,
|
||||||
|
});
|
||||||
|
|
||||||
// Create a connection pool
|
// Middleware
|
||||||
const pool = mysql.createPool(dbConfig);
|
|
||||||
|
|
||||||
app.use(cors({
|
|
||||||
origin: [
|
|
||||||
'https://inventory.kent.pw',
|
|
||||||
'http://localhost:5173',
|
|
||||||
'http://127.0.0.1:5173',
|
|
||||||
/^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/,
|
|
||||||
/^http:\/\/10\.\d+\.\d+\.\d+(:\d+)?$/
|
|
||||||
],
|
|
||||||
methods: ['GET', 'POST', 'OPTIONS'],
|
|
||||||
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
|
|
||||||
credentials: true,
|
|
||||||
exposedHeaders: ['set-cookie']
|
|
||||||
}));
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
app.use(morgan('combined'));
|
||||||
// Debug middleware to log request details
|
app.use(cors({
|
||||||
app.use((req, res, next) => {
|
origin: ['http://localhost:5173', 'https://inventory.kent.pw'],
|
||||||
console.log('Request details:', {
|
credentials: true
|
||||||
method: req.method,
|
}));
|
||||||
url: req.url,
|
|
||||||
origin: req.get('Origin'),
|
|
||||||
headers: req.headers,
|
|
||||||
body: req.body,
|
|
||||||
});
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Registration endpoint
|
|
||||||
app.post('/register', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { username, password } = req.body;
|
|
||||||
const hashedPassword = await bcrypt.hash(password, 10);
|
|
||||||
|
|
||||||
const connection = await pool.getConnection();
|
|
||||||
await connection.query('INSERT INTO users (username, password) VALUES (?, ?)', [username, hashedPassword]);
|
|
||||||
connection.release();
|
|
||||||
|
|
||||||
res.status(201).json({ message: 'User registered successfully' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Registration error:', error);
|
|
||||||
res.status(500).json({ error: 'Registration failed' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Login endpoint
|
// Login endpoint
|
||||||
app.post('/login', async (req, res) => {
|
app.post('/login', async (req, res) => {
|
||||||
|
const { username, password } = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { username, password } = req.body;
|
// Get user from database
|
||||||
console.log(`Login attempt for user: ${username}`);
|
const result = await pool.query(
|
||||||
|
'SELECT id, username, password FROM users WHERE username = $1',
|
||||||
const connection = await pool.getConnection();
|
[username]
|
||||||
const [rows] = await connection.query(
|
|
||||||
'SELECT * FROM users WHERE username = ?',
|
|
||||||
[username],
|
|
||||||
);
|
);
|
||||||
connection.release();
|
|
||||||
|
|
||||||
if (rows.length === 1) {
|
const user = result.rows[0];
|
||||||
const user = rows[0];
|
|
||||||
const passwordMatch = await bcrypt.compare(password, user.password);
|
|
||||||
|
|
||||||
if (passwordMatch) {
|
// Check if user exists and password is correct
|
||||||
console.log(`User ${username} authenticated successfully`);
|
if (!user || !(await bcrypt.compare(password, user.password))) {
|
||||||
const token = jwt.sign(
|
return res.status(401).json({ error: 'Invalid username or password' });
|
||||||
{ username: user.username },
|
|
||||||
process.env.JWT_SECRET,
|
|
||||||
{ expiresIn: '1h' },
|
|
||||||
);
|
|
||||||
res.json({ token });
|
|
||||||
} else {
|
|
||||||
console.error(`Invalid password for user: ${username}`);
|
|
||||||
res.status(401).json({ error: 'Invalid credentials' });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error(`User not found: ${username}`);
|
|
||||||
res.status(401).json({ error: 'Invalid credentials' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate JWT token
|
||||||
|
const token = jwt.sign(
|
||||||
|
{ userId: user.id, username: user.username },
|
||||||
|
process.env.JWT_SECRET,
|
||||||
|
{ expiresIn: '24h' }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({ token });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Login error:', error);
|
console.error('Login error:', error);
|
||||||
res.status(500).json({ error: 'Login failed' });
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Protected endpoint example
|
// Protected route to verify token
|
||||||
app.get('/protected', async (req, res) => {
|
app.get('/protected', async (req, res) => {
|
||||||
const authHeader = req.headers.authorization;
|
const authHeader = req.headers.authorization;
|
||||||
|
|
||||||
if (!authHeader) {
|
if (!authHeader) {
|
||||||
return res.status(401).json({ error: 'Unauthorized' });
|
return res.status(401).json({ error: 'No token provided' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = authHeader.split(' ')[1];
|
|
||||||
try {
|
try {
|
||||||
|
const token = authHeader.split(' ')[1];
|
||||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||||
|
res.json({ userId: decoded.userId, username: decoded.username });
|
||||||
// Optionally, you can fetch the user from the database here
|
|
||||||
// to verify that the user still exists or to get more user information
|
|
||||||
const connection = await pool.getConnection();
|
|
||||||
const [rows] = await connection.query('SELECT * FROM users WHERE username = ?', [decoded.username]);
|
|
||||||
connection.release();
|
|
||||||
|
|
||||||
if (rows.length === 0) {
|
|
||||||
return res.status(401).json({ error: 'User not found' });
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({ message: 'Protected resource accessed', user: decoded });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Protected endpoint error:', error);
|
console.error('Token verification error:', error);
|
||||||
res.status(403).json({ error: 'Invalid token' });
|
res.status(401).json({ error: 'Invalid token' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.listen(PORT, "0.0.0.0", () => {
|
// Health check endpoint
|
||||||
console.log(`Auth server running on port ${PORT}`);
|
app.get('/health', (req, res) => {
|
||||||
|
res.json({ status: 'healthy' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Error handling middleware
|
||||||
|
app.use((err, req, res, next) => {
|
||||||
|
console.error(err.stack);
|
||||||
|
res.status(500).json({ error: 'Something broke!' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Auth server running on port ${port}`);
|
||||||
});
|
});
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
const { Client } = require('pg');
|
|
||||||
const bcrypt = require('bcrypt');
|
|
||||||
const path = require('path');
|
|
||||||
const readline = require('readline');
|
|
||||||
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
|
|
||||||
|
|
||||||
const SALT_ROUNDS = 10;
|
|
||||||
|
|
||||||
const dbConfig = {
|
|
||||||
host: process.env.DB_HOST,
|
|
||||||
user: process.env.DB_USER,
|
|
||||||
password: process.env.DB_PASSWORD,
|
|
||||||
database: process.env.DB_NAME,
|
|
||||||
port: process.env.DB_PORT || 5432
|
|
||||||
};
|
|
||||||
|
|
||||||
function prompt(question) {
|
|
||||||
const rl = readline.createInterface({
|
|
||||||
input: process.stdin,
|
|
||||||
output: process.stdout
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
|
||||||
rl.question(question, answer => {
|
|
||||||
rl.close();
|
|
||||||
resolve(answer);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addUser() {
|
|
||||||
try {
|
|
||||||
const username = await prompt('Enter username: ');
|
|
||||||
if (!username.trim()) {
|
|
||||||
console.error('Error: Username cannot be empty');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const password = await prompt('Enter password: ');
|
|
||||||
if (!password.trim()) {
|
|
||||||
console.error('Error: Password cannot be empty');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = new Client(dbConfig);
|
|
||||||
await client.connect();
|
|
||||||
|
|
||||||
// Check if user exists
|
|
||||||
const checkUser = await client.query(
|
|
||||||
'SELECT username FROM users WHERE username = $1',
|
|
||||||
[username]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (checkUser.rows.length > 0) {
|
|
||||||
console.error('Error: Username already exists');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash password
|
|
||||||
const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
|
|
||||||
|
|
||||||
// Insert new user
|
|
||||||
await client.query(
|
|
||||||
'INSERT INTO users (username, password) VALUES ($1, $2)',
|
|
||||||
[username, hashedPassword]
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`User '${username}' created successfully`);
|
|
||||||
await client.end();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error creating user:', error.message);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run if called directly
|
|
||||||
if (require.main === module) {
|
|
||||||
addUser();
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,7 @@ export function Login() {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = isDev ? "/auth-inv/login" : `${config.authUrl}/login`;
|
const url = `${config.authUrl}/login`;
|
||||||
console.log("Making login request:", {
|
console.log("Making login request:", {
|
||||||
url,
|
url,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
Reference in New Issue
Block a user