some fixes to the auth and lockouts
This commit is contained in:
parent
a026af5b48
commit
24ce74d61c
@ -43,7 +43,6 @@ const checkTempUserExpiration = async (req, res) => {
|
||||
};
|
||||
|
||||
const requireAuth = async (req, res, next) => {
|
||||
// Check for both authentication patterns used in your app
|
||||
const isAuthenticated = (req.session && req.session.authenticated) ||
|
||||
(req.session && req.session.userId && req.session.userEmail);
|
||||
|
||||
@ -67,9 +66,22 @@ const requireAuth = async (req, res, next) => {
|
||||
logger.warn('Unauthorized access attempt', {
|
||||
ip: req.ip,
|
||||
path: req.path,
|
||||
userAgent: req.get('User-Agent')
|
||||
userAgent: req.get('User-Agent'),
|
||||
referer: req.get('Referer'), // Add referer to see where requests come from
|
||||
method: req.method,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Check if this is an auto-refresh request
|
||||
if (req.headers['x-requested-with'] === 'XMLHttpRequest' ||
|
||||
req.path.includes('/api/locations')) {
|
||||
return res.status(401).json({
|
||||
authenticated: false,
|
||||
error: 'Session expired',
|
||||
isAutoRefresh: true
|
||||
});
|
||||
}
|
||||
|
||||
if (req.xhr || req.headers.accept?.indexOf('json') > -1) {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
|
||||
@ -11,8 +11,12 @@ const keyGenerator = (req) => {
|
||||
// General API rate limiter
|
||||
const apiLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 300, // Increased from 100 to 300 to accommodate auto-refresh and multiple users
|
||||
max: 300, // Already increased
|
||||
keyGenerator,
|
||||
skip: (req) => {
|
||||
// Skip rate limiting for authenticated users (or increase their limit)
|
||||
return req.session?.authenticated === true;
|
||||
},
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
trustProxy: true, // Explicitly trust proxy
|
||||
|
||||
0
map/app/public/js/admin/auth.js
Normal file
0
map/app/public/js/admin/auth.js
Normal file
0
map/app/public/js/admin/navigation.js
Normal file
0
map/app/public/js/admin/navigation.js
Normal file
0
map/app/public/js/admin/shifts.js
Normal file
0
map/app/public/js/admin/shifts.js
Normal file
0
map/app/public/js/admin/startLocation.js
Normal file
0
map/app/public/js/admin/startLocation.js
Normal file
0
map/app/public/js/admin/users.js
Normal file
0
map/app/public/js/admin/users.js
Normal file
0
map/app/public/js/admin/utils.js
Normal file
0
map/app/public/js/admin/utils.js
Normal file
0
map/app/public/js/admin/walkSheet.js
Normal file
0
map/app/public/js/admin/walkSheet.js
Normal file
@ -96,6 +96,17 @@ function setupAutoRefresh() {
|
||||
|
||||
refreshInterval = setInterval(async () => {
|
||||
try {
|
||||
// Check if user is still authenticated
|
||||
const authResponse = await fetch('/api/auth/check');
|
||||
const authData = await authResponse.json();
|
||||
|
||||
if (!authData.authenticated) {
|
||||
// Stop auto-refresh if not authenticated
|
||||
clearInterval(refreshInterval);
|
||||
console.log('Auto-refresh stopped - user not authenticated');
|
||||
return;
|
||||
}
|
||||
|
||||
await loadLocations();
|
||||
consecutiveErrors = 0; // Reset error count on success
|
||||
} catch (error) {
|
||||
@ -111,6 +122,17 @@ function setupAutoRefresh() {
|
||||
const slowInterval = refreshInterval_ms * 2; // Double the interval
|
||||
refreshInterval = setInterval(async () => {
|
||||
try {
|
||||
// Check if user is still authenticated
|
||||
const authResponse = await fetch('/api/auth/check');
|
||||
const authData = await authResponse.json();
|
||||
|
||||
if (!authData.authenticated) {
|
||||
// Stop auto-refresh if not authenticated
|
||||
clearInterval(refreshInterval);
|
||||
console.log('Auto-refresh stopped - user not authenticated');
|
||||
return;
|
||||
}
|
||||
|
||||
await loadLocations();
|
||||
consecutiveErrors = 0;
|
||||
} catch (error) {
|
||||
|
||||
@ -126,3 +126,16 @@ export function setViewportDimensions() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add session expiry detection
|
||||
function handleAuthError(error) {
|
||||
if (error.status === 401) {
|
||||
// Stop all intervals
|
||||
if (typeof autoRefreshInterval !== 'undefined') {
|
||||
clearInterval(autoRefreshInterval);
|
||||
}
|
||||
|
||||
// Redirect to login with expiry message
|
||||
window.location.href = '/login.html?expired=true';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,293 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Listmonk Integration Test Script
|
||||
# This script validates the complete Listmonk integration
|
||||
|
||||
echo "🧪 Starting Listmonk Integration Tests..."
|
||||
echo "======================================="
|
||||
|
||||
# Configuration
|
||||
MAP_URL="http://localhost:3000"
|
||||
LISTMONK_URL="http://localhost:9000"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test counter
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
|
||||
# Helper functions
|
||||
pass_test() {
|
||||
echo -e "${GREEN}✅ PASS:${NC} $1"
|
||||
((TESTS_PASSED++))
|
||||
}
|
||||
|
||||
fail_test() {
|
||||
echo -e "${RED}❌ FAIL:${NC} $1"
|
||||
((TESTS_FAILED++))
|
||||
}
|
||||
|
||||
warn_test() {
|
||||
echo -e "${YELLOW}⚠️ WARN:${NC} $1"
|
||||
}
|
||||
|
||||
info_test() {
|
||||
echo -e "${BLUE}ℹ️ INFO:${NC} $1"
|
||||
}
|
||||
|
||||
# Test 1: Environment Configuration
|
||||
echo
|
||||
echo "📋 Test 1: Environment Configuration"
|
||||
echo "------------------------------------"
|
||||
|
||||
if grep -q "LISTMONK_ENABLED=true" .env 2>/dev/null; then
|
||||
pass_test "Listmonk is enabled in environment"
|
||||
else
|
||||
fail_test "LISTMONK_ENABLED not set to true in .env"
|
||||
fi
|
||||
|
||||
if grep -q "LISTMONK_URL=" .env 2>/dev/null; then
|
||||
LISTMONK_ENV_URL=$(grep "LISTMONK_URL=" .env | cut -d'=' -f2)
|
||||
pass_test "Listmonk URL configured: $LISTMONK_ENV_URL"
|
||||
else
|
||||
fail_test "LISTMONK_URL not configured in .env"
|
||||
fi
|
||||
|
||||
if grep -q "LISTMONK_USERNAME=" .env 2>/dev/null; then
|
||||
pass_test "Listmonk username configured"
|
||||
else
|
||||
fail_test "LISTMONK_USERNAME not configured in .env"
|
||||
fi
|
||||
|
||||
# Test 2: Service Connectivity
|
||||
echo
|
||||
echo "🌐 Test 2: Service Connectivity"
|
||||
echo "------------------------------"
|
||||
|
||||
# Test Map application
|
||||
if curl -s "$MAP_URL/health" > /dev/null 2>&1; then
|
||||
pass_test "Map application is accessible at $MAP_URL"
|
||||
else
|
||||
fail_test "Map application not accessible at $MAP_URL"
|
||||
fi
|
||||
|
||||
# Test Listmonk service
|
||||
if curl -s "$LISTMONK_URL/api/health" > /dev/null 2>&1; then
|
||||
pass_test "Listmonk service is accessible at $LISTMONK_URL"
|
||||
else
|
||||
fail_test "Listmonk service not accessible at $LISTMONK_URL"
|
||||
fi
|
||||
|
||||
# Test 3: File Structure
|
||||
echo
|
||||
echo "📁 Test 3: Integration File Structure"
|
||||
echo "------------------------------------"
|
||||
|
||||
REQUIRED_FILES=(
|
||||
"app/services/listmonk.js"
|
||||
"app/controllers/listmonkController.js"
|
||||
"app/routes/listmonk.js"
|
||||
"app/public/js/listmonk-status.js"
|
||||
"app/public/js/listmonk-admin.js"
|
||||
"app/public/css/modules/listmonk.css"
|
||||
"listmonk-env-example.txt"
|
||||
"instruct/LISTMONK_INTEGRATION_GUIDE.md"
|
||||
)
|
||||
|
||||
for file in "${REQUIRED_FILES[@]}"; do
|
||||
if [[ -f "$file" ]]; then
|
||||
pass_test "Required file exists: $file"
|
||||
else
|
||||
fail_test "Missing required file: $file"
|
||||
fi
|
||||
done
|
||||
|
||||
# Test 4: Code Integration Points
|
||||
echo
|
||||
echo "🔗 Test 4: Code Integration Points"
|
||||
echo "---------------------------------"
|
||||
|
||||
# Check if listmonk service is imported in server.js
|
||||
if grep -q "listmonk" app/server.js 2>/dev/null; then
|
||||
pass_test "Listmonk service integrated in server.js"
|
||||
else
|
||||
fail_test "Listmonk service not integrated in server.js"
|
||||
fi
|
||||
|
||||
# Check if real-time sync hooks are in controllers
|
||||
if grep -q "listmonkService" app/controllers/locationsController.js 2>/dev/null; then
|
||||
pass_test "Real-time sync integrated in locations controller"
|
||||
else
|
||||
fail_test "Real-time sync missing from locations controller"
|
||||
fi
|
||||
|
||||
if grep -q "listmonkService" app/controllers/usersController.js 2>/dev/null; then
|
||||
pass_test "Real-time sync integrated in users controller"
|
||||
else
|
||||
fail_test "Real-time sync missing from users controller"
|
||||
fi
|
||||
|
||||
# Check admin panel integration
|
||||
if grep -q "listmonk" app/public/admin.html 2>/dev/null; then
|
||||
pass_test "Admin panel includes Listmonk section"
|
||||
else
|
||||
fail_test "Admin panel missing Listmonk integration"
|
||||
fi
|
||||
|
||||
# Check status indicator integration
|
||||
if grep -q "listmonk-status" app/public/index.html 2>/dev/null; then
|
||||
pass_test "Status indicator integrated in main page"
|
||||
else
|
||||
fail_test "Status indicator not integrated in main page"
|
||||
fi
|
||||
|
||||
# Test 5: API Endpoints (if services are running)
|
||||
echo
|
||||
echo "🔌 Test 5: API Endpoints"
|
||||
echo "-----------------------"
|
||||
|
||||
# Try to test connection endpoint (requires authentication)
|
||||
STATUS_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "$MAP_URL/api/listmonk/status" 2>/dev/null)
|
||||
|
||||
if [[ "$STATUS_RESPONSE" == "401" ]]; then
|
||||
pass_test "Listmonk status endpoint exists (requires auth)"
|
||||
elif [[ "$STATUS_RESPONSE" == "200" ]]; then
|
||||
pass_test "Listmonk status endpoint accessible"
|
||||
else
|
||||
warn_test "Listmonk status endpoint response: $STATUS_RESPONSE (may require auth)"
|
||||
fi
|
||||
|
||||
# Test 6: Docker Configuration
|
||||
echo
|
||||
echo "🐳 Test 6: Docker Configuration"
|
||||
echo "------------------------------"
|
||||
|
||||
# Check if services are running
|
||||
if docker-compose ps | grep -q "map-viewer.*Up"; then
|
||||
pass_test "Map application container is running"
|
||||
else
|
||||
fail_test "Map application container not running"
|
||||
fi
|
||||
|
||||
if docker-compose ps | grep -q "listmonk.*Up"; then
|
||||
pass_test "Listmonk container is running"
|
||||
else
|
||||
fail_test "Listmonk container not running"
|
||||
fi
|
||||
|
||||
# Test 7: Documentation
|
||||
echo
|
||||
echo "📚 Test 7: Documentation"
|
||||
echo "-----------------------"
|
||||
|
||||
if grep -q "Listmonk Integration" README.md 2>/dev/null; then
|
||||
pass_test "README includes Listmonk integration documentation"
|
||||
else
|
||||
fail_test "README missing Listmonk integration documentation"
|
||||
fi
|
||||
|
||||
if grep -q "files-explainer.md" app/ 2>/dev/null && grep -q "listmonk" files-explainer.md 2>/dev/null; then
|
||||
pass_test "Files explainer includes Listmonk files"
|
||||
else
|
||||
warn_test "Files explainer may not include Listmonk files"
|
||||
fi
|
||||
|
||||
# Test 8: JavaScript Module Tests
|
||||
echo
|
||||
echo "🟨 Test 8: JavaScript Modules"
|
||||
echo "----------------------------"
|
||||
|
||||
# Check for syntax errors in JavaScript files
|
||||
JS_FILES=(
|
||||
"app/public/js/listmonk-status.js"
|
||||
"app/public/js/listmonk-admin.js"
|
||||
)
|
||||
|
||||
for js_file in "${JS_FILES[@]}"; do
|
||||
if [[ -f "$js_file" ]]; then
|
||||
# Basic syntax check (requires node)
|
||||
if command -v node > /dev/null; then
|
||||
if node -c "$js_file" 2>/dev/null; then
|
||||
pass_test "JavaScript syntax valid: $js_file"
|
||||
else
|
||||
fail_test "JavaScript syntax error: $js_file"
|
||||
fi
|
||||
else
|
||||
info_test "Node.js not available for syntax checking: $js_file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Test 9: CSS Validation
|
||||
echo
|
||||
echo "🎨 Test 9: CSS Validation"
|
||||
echo "------------------------"
|
||||
|
||||
if [[ -f "app/public/css/modules/listmonk.css" ]]; then
|
||||
# Check for basic CSS structure
|
||||
if grep -q "@media" app/public/css/modules/listmonk.css; then
|
||||
pass_test "CSS includes responsive design rules"
|
||||
else
|
||||
warn_test "CSS may not include responsive design"
|
||||
fi
|
||||
|
||||
if grep -q "\.listmonk-" app/public/css/modules/listmonk.css; then
|
||||
pass_test "CSS includes Listmonk-specific classes"
|
||||
else
|
||||
fail_test "CSS missing Listmonk-specific styling"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test 10: Log Analysis (if container is running)
|
||||
echo
|
||||
echo "📋 Test 10: Application Logs"
|
||||
echo "---------------------------"
|
||||
|
||||
if docker-compose ps | grep -q "map-viewer.*Up"; then
|
||||
# Check recent logs for Listmonk-related messages
|
||||
RECENT_LOGS=$(docker-compose logs --tail=20 map-viewer 2>/dev/null | grep -i listmonk | head -5)
|
||||
|
||||
if [[ -n "$RECENT_LOGS" ]]; then
|
||||
pass_test "Application logs contain Listmonk activity"
|
||||
info_test "Recent Listmonk log entries:"
|
||||
echo "$RECENT_LOGS" | sed 's/^/ /'
|
||||
else
|
||||
warn_test "No recent Listmonk activity in logs (may be normal)"
|
||||
fi
|
||||
|
||||
# Check for error messages
|
||||
ERROR_LOGS=$(docker-compose logs --tail=50 map-viewer 2>/dev/null | grep -i "error\|fail" | grep -i listmonk | head -3)
|
||||
|
||||
if [[ -n "$ERROR_LOGS" ]]; then
|
||||
fail_test "Found Listmonk-related errors in logs:"
|
||||
echo "$ERROR_LOGS" | sed 's/^/ /'
|
||||
else
|
||||
pass_test "No Listmonk-related errors in recent logs"
|
||||
fi
|
||||
else
|
||||
warn_test "Cannot analyze logs - Map application container not running"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo
|
||||
echo "📊 Test Summary"
|
||||
echo "=============="
|
||||
echo -e "Tests Passed: ${GREEN}$TESTS_PASSED${NC}"
|
||||
echo -e "Tests Failed: ${RED}$TESTS_FAILED${NC}"
|
||||
echo -e "Total Tests: $((TESTS_PASSED + TESTS_FAILED))"
|
||||
|
||||
if [[ $TESTS_FAILED -eq 0 ]]; then
|
||||
echo -e "${GREEN}🎉 All tests passed! Listmonk integration appears to be working correctly.${NC}"
|
||||
exit 0
|
||||
elif [[ $TESTS_FAILED -le 3 ]]; then
|
||||
echo -e "${YELLOW}⚠️ Most tests passed, but some issues were found. Check the failures above.${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "${RED}❌ Multiple test failures detected. Please review the integration setup.${NC}"
|
||||
exit 2
|
||||
fi
|
||||
@ -1,73 +0,0 @@
|
||||
// Test NocoDB API directly to debug the issue
|
||||
const axios = require('axios');
|
||||
require('dotenv').config();
|
||||
|
||||
async function testNocoDB() {
|
||||
const apiUrl = process.env.NOCODB_API_URL;
|
||||
const apiToken = process.env.NOCODB_API_TOKEN;
|
||||
const projectId = process.env.NOCODB_PROJECT_ID || 'pp1ijipzj121aqq';
|
||||
const shiftSignupsSheetId = process.env.NOCODB_SHIFT_SIGNUPS_SHEET || 'mocxv7kzcvyo4aa';
|
||||
|
||||
const client = axios.create({
|
||||
baseURL: apiUrl,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'xc-token': apiToken,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
// Test 1: Try to get table info
|
||||
console.log('Test 1: Getting table info...');
|
||||
try {
|
||||
const url = `/db/data/v1/${projectId}/${shiftSignupsSheetId}?limit=1`;
|
||||
console.log('Request URL:', apiUrl + url);
|
||||
const response = await client.get(url);
|
||||
console.log('Table info response:', JSON.stringify(response.data, null, 2));
|
||||
} catch (error) {
|
||||
console.error('Error getting table info:', error.response?.data || error.message);
|
||||
}
|
||||
|
||||
// Test 2: Try to create a minimal record
|
||||
console.log('\nTest 2: Creating minimal record...');
|
||||
try {
|
||||
const testData = {
|
||||
'Shift ID': 1,
|
||||
'User Email': 'test@example.com',
|
||||
'User Name': 'Test User',
|
||||
'Status': 'Confirmed'
|
||||
};
|
||||
|
||||
console.log('Test data:', JSON.stringify(testData, null, 2));
|
||||
|
||||
const url = `/db/data/v1/${projectId}/${shiftSignupsSheetId}`;
|
||||
console.log('Create URL:', apiUrl + url);
|
||||
|
||||
const response = await client.post(url, testData);
|
||||
console.log('Create response:', JSON.stringify(response.data, null, 2));
|
||||
} catch (error) {
|
||||
console.error('Error creating record:');
|
||||
console.error('Status:', error.response?.status);
|
||||
console.error('Data:', JSON.stringify(error.response?.data, null, 2));
|
||||
console.error('Headers:', JSON.stringify(error.response?.headers, null, 2));
|
||||
}
|
||||
|
||||
// Test 3: Check what table fields exist
|
||||
console.log('\nTest 3: Getting table schema...');
|
||||
try {
|
||||
const schemaUrl = `/db/meta/projects/${projectId}/tables`;
|
||||
const response = await client.get(schemaUrl);
|
||||
const tables = response.data.list || [];
|
||||
const signupsTable = tables.find(t => t.id === shiftSignupsSheetId);
|
||||
if (signupsTable) {
|
||||
console.log('Signups table columns:');
|
||||
signupsTable.columns?.forEach(col => {
|
||||
console.log(` - ${col.title} (${col.column_name}) - ${col.uidt} - PK: ${col.pk}`);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting schema:', error.response?.data || error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testNocoDB().catch(console.error);
|
||||
Loading…
x
Reference in New Issue
Block a user