Clean up development artifacts and obsolete code

Dependencies:
- Remove unused bcrypt package (only bcryptjs is used)
- Remove @types/bcrypt (only @types/bcryptjs needed)

Scripts cleanup:
- Delete migration scripts (one-time use, already applied):
  - add-mqtt-tables.js
  - add-parent-user-column.js
  - migrate-device-ownership.js
  - fix-acl-topic-patterns.js
  - update-acl-permission.js
- Delete personal test/debug scripts:
  - reset-joachim-password.js
  - test-joachim-password.js
  - check-admin.js
  - check-user-password.js
  - test-password.js
  - test-device-access.js
  - test-user-visibility.js
- Move change-mqtt-admin-password.sh to scripts/ directory

Code cleanup:
- Remove debug console.log statements from:
  - app/api/locations/ingest/route.ts
  - components/map/MapView.tsx (2 debug logs)
  - lib/db.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-01 09:03:18 +00:00
parent 7aab1ab789
commit e64a22dee5
17 changed files with 0 additions and 797 deletions

View File

@@ -36,15 +36,6 @@ export async function POST(request: NextRequest) {
); );
} }
// Debug logging for speed and battery values
console.log('[Ingest Debug] Received locations:', locations.map(loc => ({
username: loc.username,
speed: loc.speed,
speed_type: typeof loc.speed,
battery: loc.battery,
battery_type: typeof loc.battery
})));
// Validate required fields // Validate required fields
for (const loc of locations) { for (const loc of locations) {
if (!loc.latitude || !loc.longitude || !loc.timestamp) { if (!loc.latitude || !loc.longitude || !loc.timestamp) {

View File

@@ -164,18 +164,7 @@ export default function MapView({ selectedDevice, timeFilter, isPaused, filterMo
const data: LocationResponse = await response.json(); const data: LocationResponse = await response.json();
// Debug: Log last 3 locations to see speed/battery values
if (data.history && data.history.length > 0) { if (data.history && data.history.length > 0) {
console.log('[MapView Debug] Last 3 locations:', data.history.slice(0, 3).map(loc => ({
username: loc.username,
timestamp: loc.timestamp,
speed: loc.speed,
speed_type: typeof loc.speed,
speed_is_null: loc.speed === null,
speed_is_undefined: loc.speed === undefined,
battery: loc.battery,
})));
// Auto-center to latest location // Auto-center to latest location
const latest = data.history[0]; const latest = data.history[0];
if (latest && latest.latitude && latest.longitude) { if (latest && latest.latitude && latest.longitude) {
@@ -301,18 +290,6 @@ export default function MapView({ selectedDevice, timeFilter, isPaused, filterMo
// Calculate z-index: newer locations get higher z-index // Calculate z-index: newer locations get higher z-index
const zIndexOffset = sortedLocs.length - idx; const zIndexOffset = sortedLocs.length - idx;
// Debug: Log for latest location only
if (isLatest) {
console.log('[Popup Debug] Latest location for', device.name, {
speed: loc.speed,
speed_type: typeof loc.speed,
speed_is_null: loc.speed === null,
speed_is_undefined: loc.speed === undefined,
condition_result: loc.speed != null,
display_time: loc.display_time
});
}
return ( return (
<Marker <Marker
key={`${deviceId}-${loc.timestamp}-${idx}`} key={`${deviceId}-${loc.timestamp}-${idx}`}

View File

@@ -376,15 +376,6 @@ export const locationDb = {
const batteryValue = loc.battery !== undefined && loc.battery !== null ? Number(loc.battery) : null; const batteryValue = loc.battery !== undefined && loc.battery !== null ? Number(loc.battery) : null;
const speedValue = loc.speed !== undefined && loc.speed !== null ? Number(loc.speed) : null; const speedValue = loc.speed !== undefined && loc.speed !== null ? Number(loc.speed) : null;
// Debug log
console.log('[DB Insert Debug]', {
username: loc.username,
speed_in: loc.speed,
speed_out: speedValue,
battery_in: loc.battery,
battery_out: batteryValue
});
const result = stmt.run( const result = stmt.run(
loc.latitude, loc.latitude,
loc.longitude, loc.longitude,

View File

@@ -23,13 +23,11 @@
"dependencies": { "dependencies": {
"@react-email/components": "^0.5.7", "@react-email/components": "^0.5.7",
"@tailwindcss/postcss": "^4.1.17", "@tailwindcss/postcss": "^4.1.17",
"@types/bcrypt": "^6.0.0",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/leaflet": "^1.9.21", "@types/leaflet": "^1.9.21",
"@types/node": "^24.10.1", "@types/node": "^24.10.1",
"@types/react": "^19.2.4", "@types/react": "^19.2.4",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"bcrypt": "^6.0.0",
"bcryptjs": "^3.0.3", "bcryptjs": "^3.0.3",
"better-sqlite3": "^12.4.1", "better-sqlite3": "^12.4.1",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",

View File

@@ -1,103 +0,0 @@
#!/usr/bin/env node
/**
* Migration script to add MQTT credentials and ACL tables
* This extends the existing database with MQTT provisioning capabilities
*/
const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');
const dataDir = path.join(__dirname, '..', 'data');
const dbPath = path.join(dataDir, 'database.sqlite');
// Check if database exists
if (!fs.existsSync(dbPath)) {
console.error('❌ Database not found. Run npm run db:init:app first');
process.exit(1);
}
// Open database
const db = new Database(dbPath);
db.pragma('journal_mode = WAL');
console.log('Starting MQTT tables migration...\n');
// Create mqtt_credentials table
db.exec(`
CREATE TABLE IF NOT EXISTS mqtt_credentials (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL UNIQUE,
mqtt_username TEXT NOT NULL UNIQUE,
mqtt_password_hash TEXT NOT NULL,
enabled INTEGER DEFAULT 1,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (device_id) REFERENCES Device(id) ON DELETE CASCADE,
CHECK (enabled IN (0, 1))
);
`);
console.log('✓ Created mqtt_credentials table');
// Create mqtt_acl_rules table
db.exec(`
CREATE TABLE IF NOT EXISTS mqtt_acl_rules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL,
topic_pattern TEXT NOT NULL,
permission TEXT NOT NULL CHECK(permission IN ('read', 'write', 'readwrite')),
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (device_id) REFERENCES Device(id) ON DELETE CASCADE
);
`);
console.log('✓ Created mqtt_acl_rules table');
// Create indexes for performance
db.exec(`
CREATE INDEX IF NOT EXISTS idx_mqtt_credentials_device
ON mqtt_credentials(device_id);
CREATE INDEX IF NOT EXISTS idx_mqtt_credentials_username
ON mqtt_credentials(mqtt_username);
CREATE INDEX IF NOT EXISTS idx_mqtt_acl_device
ON mqtt_acl_rules(device_id);
`);
console.log('✓ Created indexes');
// Create mqtt_sync_status table to track pending changes
db.exec(`
CREATE TABLE IF NOT EXISTS mqtt_sync_status (
id INTEGER PRIMARY KEY CHECK (id = 1),
pending_changes INTEGER DEFAULT 0,
last_sync_at TEXT,
last_sync_status TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
`);
console.log('✓ Created mqtt_sync_status table');
// Initialize sync status
const syncStatus = db.prepare('SELECT * FROM mqtt_sync_status WHERE id = 1').get();
if (!syncStatus) {
db.prepare(`
INSERT INTO mqtt_sync_status (id, pending_changes, last_sync_status)
VALUES (1, 0, 'never_synced')
`).run();
console.log('✓ Initialized sync status');
} else {
console.log('✓ Sync status already exists');
}
// Get stats
const mqttCredsCount = db.prepare('SELECT COUNT(*) as count FROM mqtt_credentials').get();
const aclRulesCount = db.prepare('SELECT COUNT(*) as count FROM mqtt_acl_rules').get();
console.log(`\n✓ MQTT tables migration completed successfully!`);
console.log(` MQTT Credentials: ${mqttCredsCount.count}`);
console.log(` ACL Rules: ${aclRulesCount.count}`);
db.close();

View File

@@ -1,49 +0,0 @@
#!/usr/bin/env node
/**
* Add parent_user_id column to User table
* This enables parent-child relationship for ADMIN -> VIEWER hierarchy
*/
const Database = require('better-sqlite3');
const path = require('path');
const dataDir = path.join(__dirname, '..', 'data');
const dbPath = path.join(dataDir, 'database.sqlite');
console.log('Adding parent_user_id column to User table...');
const db = new Database(dbPath);
try {
// Check if column already exists
const tableInfo = db.prepare("PRAGMA table_info(User)").all();
const hasParentColumn = tableInfo.some(col => col.name === 'parent_user_id');
if (hasParentColumn) {
console.log('⚠ Column parent_user_id already exists, skipping...');
} else {
// Add parent_user_id column
db.exec(`
ALTER TABLE User ADD COLUMN parent_user_id TEXT;
`);
console.log('✓ Added parent_user_id column');
// Create index for faster lookups
db.exec(`
CREATE INDEX IF NOT EXISTS idx_user_parent ON User(parent_user_id);
`);
console.log('✓ Created index on parent_user_id');
// Add foreign key constraint check
// Note: SQLite doesn't enforce foreign keys on ALTER TABLE,
// but we'll add the constraint in the application logic
console.log('✓ Parent-child relationship enabled');
}
db.close();
console.log('\n✓ Migration completed successfully!');
} catch (error) {
console.error('Error during migration:', error);
db.close();
process.exit(1);
}

View File

@@ -1,51 +0,0 @@
#!/usr/bin/env node
/**
* Check admin user and test password verification
*/
const Database = require('better-sqlite3');
const bcrypt = require('bcryptjs');
const path = require('path');
const dbPath = path.join(__dirname, '..', 'data', 'database.sqlite');
const db = new Database(dbPath);
// Get admin user
const user = db.prepare('SELECT * FROM User WHERE username = ?').get('admin');
if (!user) {
console.log('❌ Admin user not found!');
process.exit(1);
}
console.log('Admin user found:');
console.log(' ID:', user.id);
console.log(' Username:', user.username);
console.log(' Email:', user.email);
console.log(' Role:', user.role);
console.log(' Password Hash:', user.passwordHash.substring(0, 20) + '...');
// Test password verification
const testPassword = 'admin123';
console.log('\nTesting password verification...');
console.log(' Test password:', testPassword);
try {
const isValid = bcrypt.compareSync(testPassword, user.passwordHash);
console.log(' Result:', isValid ? '✅ VALID' : '❌ INVALID');
if (!isValid) {
console.log('\n⚠ Password verification failed!');
console.log('Recreating admin user with fresh hash...');
const newHash = bcrypt.hashSync(testPassword, 10);
db.prepare('UPDATE User SET passwordHash = ? WHERE username = ?').run(newHash, 'admin');
console.log('✅ Admin password reset successfully');
console.log('Try logging in again with: admin / admin123');
}
} catch (error) {
console.log(' Error:', error.message);
}
db.close();

View File

@@ -1,63 +0,0 @@
#!/usr/bin/env node
/**
* Check user password hash in database
* Usage: node scripts/check-user-password.js <username>
*/
const Database = require('better-sqlite3');
const bcrypt = require('bcryptjs');
const path = require('path');
const dbPath = path.join(process.cwd(), 'data', 'database.sqlite');
async function checkUserPassword(username) {
const db = new Database(dbPath, { readonly: true });
try {
const user = db.prepare('SELECT * FROM User WHERE username = ?').get(username);
if (!user) {
console.error(`❌ User "${username}" not found`);
process.exit(1);
}
console.log(`\n✓ User found: ${user.username}`);
console.log(` ID: ${user.id}`);
console.log(` Email: ${user.email || 'N/A'}`);
console.log(` Role: ${user.role}`);
console.log(` Created: ${user.createdAt}`);
console.log(` Updated: ${user.updatedAt}`);
console.log(` Last Login: ${user.lastLoginAt || 'Never'}`);
console.log(`\n Password Hash: ${user.passwordHash.substring(0, 60)}...`);
console.log(` Hash starts with: ${user.passwordHash.substring(0, 7)}`);
// Check if it's a valid bcrypt hash
const isBcrypt = user.passwordHash.startsWith('$2a$') ||
user.passwordHash.startsWith('$2b$') ||
user.passwordHash.startsWith('$2y$');
if (isBcrypt) {
console.log(` ✓ Hash format: Valid bcrypt hash`);
// Extract rounds
const rounds = parseInt(user.passwordHash.split('$')[2]);
console.log(` ✓ Bcrypt rounds: ${rounds}`);
} else {
console.log(` ❌ Hash format: NOT a valid bcrypt hash!`);
}
} catch (error) {
console.error('Error:', error);
process.exit(1);
} finally {
db.close();
}
}
const username = process.argv[2];
if (!username) {
console.error('Usage: node scripts/check-user-password.js <username>');
process.exit(1);
}
checkUserPassword(username);

View File

@@ -1,88 +0,0 @@
#!/usr/bin/env node
/**
* Migration script to fix ACL topic patterns
*
* Changes: owntracks/owntrack/<device_id> → owntracks/<username>/#
*
* This script:
* 1. Finds all ACL rules with the old pattern
* 2. Looks up the correct MQTT username for each device
* 3. Updates the topic_pattern to use the username
*/
const Database = require('better-sqlite3');
const path = require('path');
const dbPath = path.join(__dirname, '..', 'tracker.db');
const db = new Database(dbPath);
console.log('🔧 Fixing ACL topic patterns...\n');
try {
// Get all ACL rules with the old pattern
const aclRules = db.prepare(`
SELECT id, device_id, topic_pattern, permission
FROM mqtt_acl_rules
WHERE topic_pattern LIKE 'owntracks/owntrack/%'
`).all();
console.log(`Found ${aclRules.length} ACL rules to fix\n`);
if (aclRules.length === 0) {
console.log('✓ No ACL rules need fixing!');
db.close();
process.exit(0);
}
let fixed = 0;
let failed = 0;
for (const rule of aclRules) {
// Look up the MQTT username for this device
const credential = db.prepare(`
SELECT mqtt_username
FROM mqtt_credentials
WHERE device_id = ?
`).get(rule.device_id);
if (!credential) {
console.log(`⚠ Warning: No MQTT credentials found for device ${rule.device_id}, skipping...`);
failed++;
continue;
}
const oldPattern = rule.topic_pattern;
const newPattern = `owntracks/${credential.mqtt_username}/#`;
// Update the ACL rule
db.prepare(`
UPDATE mqtt_acl_rules
SET topic_pattern = ?
WHERE id = ?
`).run(newPattern, rule.id);
console.log(`✓ Fixed rule for device ${rule.device_id}:`);
console.log(` Old: ${oldPattern}`);
console.log(` New: ${newPattern}\n`);
fixed++;
}
// Mark pending changes for MQTT sync
db.prepare(`
UPDATE mqtt_sync_status
SET pending_changes = pending_changes + 1
`).run();
console.log('─'.repeat(50));
console.log(`\n✅ Migration complete!`);
console.log(` Fixed: ${fixed}`);
console.log(` Failed: ${failed}`);
console.log(`\n⚠️ Run MQTT Sync to apply changes to Mosquitto broker`);
} catch (error) {
console.error('❌ Error during migration:', error);
process.exit(1);
} finally {
db.close();
}

View File

@@ -1,75 +0,0 @@
#!/usr/bin/env node
/**
* Migrate devices with NULL ownerId to admin user
* Usage: node scripts/migrate-device-ownership.js
*/
const Database = require('better-sqlite3');
const path = require('path');
const dbPath = path.join(process.cwd(), 'data', 'database.sqlite');
function migrateDeviceOwnership() {
const db = new Database(dbPath);
try {
// Find first admin user
const adminUser = db.prepare(`
SELECT * FROM User
WHERE role = 'ADMIN'
ORDER BY createdAt ASC
LIMIT 1
`).get();
if (!adminUser) {
console.error('❌ No admin user found in database');
process.exit(1);
}
console.log(`✓ Admin user found: ${adminUser.username} (${adminUser.id})`);
// Find devices with NULL ownerId
const devicesWithoutOwner = db.prepare(`
SELECT * FROM Device
WHERE ownerId IS NULL
`).all();
console.log(`\n📊 Found ${devicesWithoutOwner.length} devices without owner`);
if (devicesWithoutOwner.length === 0) {
console.log('✓ All devices already have an owner');
return;
}
// Update devices to assign to admin
const updateStmt = db.prepare(`
UPDATE Device
SET ownerId = ?, updatedAt = datetime('now')
WHERE ownerId IS NULL
`);
const result = updateStmt.run(adminUser.id);
console.log(`\n✅ Updated ${result.changes} devices`);
console.log(` Assigned to: ${adminUser.username} (${adminUser.id})`);
// Show updated devices
const updatedDevices = db.prepare(`
SELECT id, name, ownerId FROM Device
WHERE ownerId = ?
`).all(adminUser.id);
console.log('\n📋 Devices now owned by admin:');
updatedDevices.forEach(device => {
console.log(` - ${device.id}: ${device.name}`);
});
} catch (error) {
console.error('❌ Error:', error);
process.exit(1);
} finally {
db.close();
}
}
migrateDeviceOwnership();

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env node
const bcrypt = require('bcryptjs');
const path = require('path');
const Database = require('better-sqlite3');
const dbPath = path.join(process.cwd(), 'data', 'database.sqlite');
const db = new Database(dbPath);
const newPassword = 'joachim123';
const passwordHash = bcrypt.hashSync(newPassword, 10);
const result = db.prepare('UPDATE User SET passwordHash = ? WHERE username = ?').run(passwordHash, 'joachim');
if (result.changes > 0) {
console.log('✓ Password reset successfully for user "joachim"');
console.log(` New password: ${newPassword}`);
} else {
console.log('❌ User "joachim" not found');
}
db.close();

View File

@@ -1,89 +0,0 @@
#!/usr/bin/env node
/**
* Test device access control after security fix
* Tests that users can only see devices they own
*/
const Database = require('better-sqlite3');
const path = require('path');
const dbPath = path.join(__dirname, '..', 'data', 'database.sqlite');
const db = new Database(dbPath);
// Import the getAllowedDeviceIds logic
function getAllowedDeviceIds(userId, role, username) {
try {
// Super admin (username === "admin") can see ALL devices
if (username === 'admin') {
const allDevices = db.prepare('SELECT id FROM Device WHERE isActive = 1').all();
return allDevices.map(d => d.id);
}
// VIEWER users see their parent user's devices
if (role === 'VIEWER') {
const user = db.prepare('SELECT parent_user_id FROM User WHERE id = ?').get(userId);
if (user?.parent_user_id) {
const devices = db.prepare('SELECT id FROM Device WHERE ownerId = ? AND isActive = 1').all(user.parent_user_id);
return devices.map(d => d.id);
}
// If VIEWER has no parent, return empty array
return [];
}
// Regular ADMIN users see only their own devices
if (role === 'ADMIN') {
const devices = db.prepare('SELECT id FROM Device WHERE ownerId = ? AND isActive = 1').all(userId);
return devices.map(d => d.id);
}
// Default: no access
return [];
} catch (error) {
console.error('Error in getAllowedDeviceIds:', error);
return [];
}
}
console.log('=== Device Access Control Test ===\n');
// Get all users
const users = db.prepare('SELECT id, username, role, parent_user_id FROM User').all();
// Get all devices
const allDevices = db.prepare('SELECT id, name, ownerId FROM Device WHERE isActive = 1').all();
console.log('All devices in system:');
allDevices.forEach(d => {
console.log(` - Device ${d.id} (${d.name}) owned by: ${d.ownerId}`);
});
console.log('');
// Test each user
users.forEach(user => {
const allowedDevices = getAllowedDeviceIds(user.id, user.role, user.username);
console.log(`User: ${user.username} (${user.role})`);
console.log(` ID: ${user.id}`);
if (user.parent_user_id) {
const parent = users.find(u => u.id === user.parent_user_id);
console.log(` Parent: ${parent?.username || 'unknown'}`);
}
console.log(` Can see devices: ${allowedDevices.length > 0 ? allowedDevices.join(', ') : 'NONE'}`);
// Show device names
if (allowedDevices.length > 0) {
allowedDevices.forEach(deviceId => {
const device = allDevices.find(d => d.id === deviceId);
console.log(` - ${deviceId}: ${device?.name || 'unknown'}`);
});
}
console.log('');
});
console.log('=== Expected Results ===');
console.log('✓ admin: Should see ALL devices (10, 11, 12, 15)');
console.log('✓ joachim: Should see only devices 12, 15 (owned by joachim)');
console.log('✓ hummel: Should see devices 12, 15 (parent joachim\'s devices)');
console.log('✓ joachiminfo: Should see NO devices (doesn\'t own any)');
db.close();

View File

@@ -1,25 +0,0 @@
#!/usr/bin/env node
const bcrypt = require('bcryptjs');
const path = require('path');
const Database = require('better-sqlite3');
const dbPath = path.join(process.cwd(), 'data', 'database.sqlite');
const db = new Database(dbPath);
const joachim = db.prepare('SELECT * FROM User WHERE username = ?').get('joachim');
if (!joachim) {
console.log('❌ User "joachim" not found');
process.exit(1);
}
console.log('Testing passwords for joachim:');
const passwords = ['joachim123', 'joachim', 'password', 'admin123'];
for (const pwd of passwords) {
const match = bcrypt.compareSync(pwd, joachim.passwordHash);
console.log(` '${pwd}': ${match ? '✓ MATCH' : '✗ no match'}`);
}
db.close();

View File

@@ -1,55 +0,0 @@
#!/usr/bin/env node
/**
* Test if a password matches a user's stored hash
* Usage: node scripts/test-password.js <username> <password>
*/
const Database = require('better-sqlite3');
const bcrypt = require('bcryptjs');
const path = require('path');
const dbPath = path.join(process.cwd(), 'data', 'database.sqlite');
async function testPassword(username, password) {
const db = new Database(dbPath, { readonly: true });
try {
const user = db.prepare('SELECT * FROM User WHERE username = ?').get(username);
if (!user) {
console.error(`❌ User "${username}" not found`);
process.exit(1);
}
console.log(`\n✓ User found: ${user.username}`);
console.log(` Testing password...`);
const isValid = await bcrypt.compare(password, user.passwordHash);
if (isValid) {
console.log(` ✅ Password is CORRECT!`);
} else {
console.log(` ❌ Password is INCORRECT!`);
console.log(`\n Debug info:`);
console.log(` - Password provided: "${password}"`);
console.log(` - Password length: ${password.length}`);
console.log(` - Hash in DB: ${user.passwordHash.substring(0, 60)}...`);
}
} catch (error) {
console.error('Error:', error);
process.exit(1);
} finally {
db.close();
}
}
const username = process.argv[2];
const password = process.argv[3];
if (!username || !password) {
console.error('Usage: node scripts/test-password.js <username> <password>');
process.exit(1);
}
testPassword(username, password);

View File

@@ -1,101 +0,0 @@
#!/usr/bin/env node
/**
* Test script to verify user visibility restrictions
* - Admin can see all users including "admin"
* - Non-admin users (like "joachim") cannot see the "admin" user
*/
const baseUrl = 'http://localhost:3001';
async function login(username, password) {
const response = await fetch(`${baseUrl}/api/auth/signin`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
});
const cookies = response.headers.get('set-cookie');
return cookies;
}
async function getUsers(cookies) {
const response = await fetch(`${baseUrl}/api/users`, {
headers: {
'Cookie': cookies,
},
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Failed to get users: ${error.error}`);
}
const data = await response.json();
return data.users;
}
async function testAdminUser() {
console.log('\n🔍 Testing with "admin" user:');
console.log('================================');
try {
const cookies = await login('admin', 'admin123');
const users = await getUsers(cookies);
console.log(`✓ Admin can see ${users.length} user(s):`);
users.forEach(user => {
console.log(` - ${user.username} (${user.role})`);
});
const hasAdminUser = users.some(u => u.username === 'admin');
if (hasAdminUser) {
console.log('✓ Admin can see the "admin" user ✓');
} else {
console.log('✗ FAIL: Admin cannot see the "admin" user');
}
} catch (error) {
console.log(`✗ FAIL: ${error.message}`);
}
}
async function testJoachimUser() {
console.log('\n🔍 Testing with "joachim" user:');
console.log('================================');
try {
const cookies = await login('joachim', 'joachim123');
const users = await getUsers(cookies);
console.log(`✓ Joachim can see ${users.length} user(s):`);
users.forEach(user => {
console.log(` - ${user.username} (${user.role})`);
});
const hasAdminUser = users.some(u => u.username === 'admin');
if (!hasAdminUser) {
console.log('✓ Joachim cannot see the "admin" user ✓');
} else {
console.log('✗ FAIL: Joachim can see the "admin" user (should be hidden)');
}
} catch (error) {
console.log(`✗ FAIL: ${error.message}`);
}
}
async function main() {
console.log('Testing User Visibility Restrictions');
console.log('=====================================\n');
console.log('Expected behavior:');
console.log(' - Admin user can see all users including "admin"');
console.log(' - Non-admin users (joachim) cannot see "admin" user\n');
await testAdminUser();
await testJoachimUser();
console.log('\n✓ Test completed!');
}
main().catch(console.error);

View File

@@ -1,33 +0,0 @@
#!/usr/bin/env node
/**
* Update ACL rule permission to readwrite
*/
const Database = require('better-sqlite3');
const path = require('path');
const dbPath = path.join(process.cwd(), 'data', 'database.sqlite');
const db = new Database(dbPath);
try {
// Update ACL rule 11 to readwrite
db.prepare('UPDATE mqtt_acl_rules SET permission = ? WHERE id = ?').run('readwrite', 11);
// Mark pending changes
db.prepare(`UPDATE mqtt_sync_status
SET pending_changes = pending_changes + 1,
updated_at = datetime('now')
WHERE id = 1`).run();
console.log('✓ ACL rule updated to readwrite');
const updated = db.prepare('SELECT * FROM mqtt_acl_rules WHERE id = ?').get(11);
console.log('\nUpdated rule:');
console.log(JSON.stringify(updated, null, 2));
} catch (error) {
console.error('Error:', error);
process.exit(1);
} finally {
db.close();
}