Added in a password field for login. need to add encryption sometime

This commit is contained in:
admin 2025-07-09 10:05:12 -06:00
parent ab2e91ec12
commit d11837e449
3 changed files with 265 additions and 47 deletions

View File

@ -163,6 +163,18 @@
>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
placeholder="Enter your password"
required
autocomplete="current-password"
>
</div>
<button type="submit" class="login-button" id="login-button">
Sign In
</button>
@ -180,6 +192,7 @@
e.preventDefault();
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
const button = document.getElementById('login-button');
const errorMessage = document.getElementById('error-message');
const successMessage = document.getElementById('success-message');
@ -198,7 +211,7 @@
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
body: JSON.stringify({ email, password }),
credentials: 'include'
});

View File

@ -311,12 +311,12 @@ app.post('/api/auth/login', authLimiter, async (req, res) => {
userAgent: req.headers['user-agent']
});
const { email } = req.body;
const { email, password } = req.body;
if (!email) {
if (!email || !password) {
return res.status(400).json({
success: false,
error: 'Email is required'
error: 'Email and password are required'
});
}
@ -338,7 +338,7 @@ app.post('/api/auth/login', authLimiter, async (req, res) => {
});
}
// Fetch authorized emails from NocoDB
// Fetch user from NocoDB
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}`;
logger.info(`Checking authentication for email: ${email}`);
@ -350,54 +350,79 @@ app.post('/api/auth/login', authLimiter, async (req, res) => {
'Content-Type': 'application/json'
},
params: {
limit: 1000 // Adjust if you have more authorized users
where: `(Email,eq,${email})`,
limit: 1
}
});
const users = response.data.list || [];
// Check if email exists in the authorized users list
const authorizedUser = users.find(user =>
user.Email && user.Email.toLowerCase() === email.toLowerCase()
);
if (authorizedUser) {
// Set session including admin status
req.session.authenticated = true;
req.session.userEmail = email;
req.session.userName = authorizedUser.Name || email;
req.session.isAdmin = authorizedUser.Admin === true || authorizedUser.Admin === 1;
// Force session save before sending response
req.session.save((err) => {
if (err) {
logger.error('Session save error:', err);
return res.status(500).json({
success: false,
error: 'Session error. Please try again.'
});
}
logger.info(`User authenticated: ${email}, Admin: ${req.session.isAdmin}`);
res.json({
success: true,
message: 'Login successful',
user: {
email: email,
name: req.session.userName,
isAdmin: req.session.isAdmin
}
});
});
} else {
logger.warn(`Authentication failed for email: ${email}`);
res.status(401).json({
if (users.length === 0) {
logger.warn(`No user found with email: ${email}`);
return res.status(401).json({
success: false,
error: 'Email not authorized. Please contact an administrator.'
error: 'Invalid email or password'
});
}
const user = users[0];
// Check password (plain text comparison for now)
if (user.Password !== password && user.password !== password) {
logger.warn(`Invalid password for email: ${email}`);
return res.status(401).json({
success: false,
error: 'Invalid email or password'
});
}
// Update last login time
try {
const updateUrl = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}/${user.Id || user.id || user.ID}`;
await axios.patch(updateUrl, {
'Last Login': new Date().toISOString(),
last_login: new Date().toISOString()
}, {
headers: {
'xc-token': process.env.NOCODB_API_TOKEN,
'Content-Type': 'application/json'
}
});
} catch (updateError) {
logger.warn('Failed to update last login time:', updateError.message);
// Don't fail the login if we can't update last login time
}
// Set session including admin status
req.session.authenticated = true;
req.session.userEmail = email;
req.session.userName = user.Name || user.name || email;
req.session.isAdmin = user.Admin === true || user.Admin === 1 || user.admin === true || user.admin === 1;
req.session.userId = user.Id || user.id || user.ID;
// Force session save before sending response
req.session.save((err) => {
if (err) {
logger.error('Session save error:', err);
return res.status(500).json({
success: false,
error: 'Session error. Please try again.'
});
}
logger.info(`User authenticated: ${email}, Admin: ${req.session.isAdmin}`);
res.json({
success: true,
message: 'Login successful',
user: {
email: email,
name: req.session.userName,
isAdmin: req.session.isAdmin
}
});
});
} catch (error) {
logger.error('Login error:', error.message);
res.status(500).json({
@ -1823,6 +1848,172 @@ app.get('/api/debug/walk-sheet-raw', requireAdmin, async (req, res) => {
}
});
// Admin user management endpoints
app.get('/api/admin/users', requireAdmin, async (req, res) => {
try {
if (!LOGIN_SHEET_ID) {
return res.status(500).json({
success: false,
error: 'Login sheet not configured'
});
}
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}`;
const response = await axios.get(url, {
headers: {
'xc-token': process.env.NOCODB_API_TOKEN,
'Content-Type': 'application/json'
},
params: {
limit: 100,
sort: '-created_at'
}
});
const users = response.data.list || [];
// Remove password field from response for security
const safeUsers = users.map(user => {
const { Password, password, ...safeUser } = user;
return safeUser;
});
res.json({
success: true,
users: safeUsers
});
} catch (error) {
logger.error('Error fetching users:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch users'
});
}
});
app.post('/api/admin/users', requireAdmin, async (req, res) => {
try {
const { email, password, name, admin } = req.body;
if (!email || !password) {
return res.status(400).json({
success: false,
error: 'Email and password are required'
});
}
if (!LOGIN_SHEET_ID) {
return res.status(500).json({
success: false,
error: 'Login sheet not configured'
});
}
// Check if user already exists
const checkUrl = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}`;
const checkResponse = await axios.get(checkUrl, {
headers: {
'xc-token': process.env.NOCODB_API_TOKEN,
'Content-Type': 'application/json'
},
params: {
where: `(Email,eq,${email})`,
limit: 1
}
});
if (checkResponse.data.list && checkResponse.data.list.length > 0) {
return res.status(400).json({
success: false,
error: 'User with this email already exists'
});
}
// Create new user
const userData = {
Email: email,
email: email,
Password: password,
password: password,
Name: name || '',
name: name || '',
Admin: admin === true,
admin: admin === true,
'Created At': new Date().toISOString(),
created_at: new Date().toISOString()
};
const response = await axios.post(checkUrl, userData, {
headers: {
'xc-token': process.env.NOCODB_API_TOKEN,
'Content-Type': 'application/json'
}
});
res.status(201).json({
success: true,
message: 'User created successfully',
user: {
id: response.data.Id || response.data.id || response.data.ID,
email: email,
name: name,
admin: admin
}
});
} catch (error) {
logger.error('Error creating user:', error);
res.status(500).json({
success: false,
error: 'Failed to create user'
});
}
});
app.delete('/api/admin/users/:id', requireAdmin, async (req, res) => {
try {
const userId = req.params.id;
if (!LOGIN_SHEET_ID) {
return res.status(500).json({
success: false,
error: 'Login sheet not configured'
});
}
// Don't allow admins to delete themselves
if (userId === req.session.userId) {
return res.status(400).json({
success: false,
error: 'Cannot delete your own account'
});
}
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}/${userId}`;
await axios.delete(url, {
headers: {
'xc-token': process.env.NOCODB_API_TOKEN
}
});
res.json({
success: true,
message: 'User deleted successfully'
});
} catch (error) {
logger.error('Error deleting user:', error);
res.status(500).json({
success: false,
error: 'Failed to delete user'
});
}
});
// Error handling middleware
app.use((err, req, res, next) => {
logger.error('Unhandled error:', err);

View File

@ -411,6 +411,12 @@ create_login_table() {
"uidt": "Email",
"rqd": true
},
{
"column_name": "password",
"title": "Password",
"uidt": "SingleLineText",
"rqd": true
},
{
"column_name": "name",
"title": "Name",
@ -563,9 +569,10 @@ create_default_admin() {
local login_table_id=$2
print_status "Creating default admin user..."
local admin_data='{
"email": "admin@thebunkerops.ca",
"password": "admin123",
"name": "Administrator",
"admin": true,
"created_at": "'"$(date -u +"%Y-%m-%d %H:%M:%S")"'"
@ -663,8 +670,15 @@ main() {
print_status " - NOCODB_VIEW_URL (for locations table)"
print_status " - NOCODB_LOGIN_SHEET (for login table)"
print_status " - NOCODB_SETTINGS_SHEET (for settings table)"
print_status "4. The default admin user is: admin@thebunkerops.ca"
print_status "5. Start adding your location data!"
print_status "4. The default admin user is: admin@thebunkerops.ca with password: admin123"
print_status "5. IMPORTANT: Change the default password after first login!"
print_status "6. Start adding your location data!"
print_warning ""
print_warning "IMPORTANT: This script created a NEW base. Your existing data was NOT modified."
print_warning "Please update your .env file with the new table URLs from the newly created base."
print_warning "SECURITY: Change the default admin password immediately after first login!"
print_warning ""
print_warning "IMPORTANT: This script created a NEW base. Your existing data was NOT modified."