Added in a password field for login. need to add encryption sometime
This commit is contained in:
@@ -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'
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user