WE LOVE TESTING ON PROD
This commit is contained in:
parent
cc62815961
commit
a52a10378c
72
index.js
72
index.js
|
@ -234,6 +234,78 @@ app.post('/admin/login', (req, res) => {
|
|||
});
|
||||
})
|
||||
|
||||
app.get("/admin/register/:inviteCode", async (req, res) => {
|
||||
const inviteCode = req.params.inviteCode;
|
||||
if (!inviteCode) {
|
||||
res.status(400).send('Bad Request');
|
||||
return;
|
||||
}
|
||||
|
||||
const inviteData = await pool.query("SELECT * FROM admin_invites WHERE code = ?", [inviteCode]);
|
||||
if (!inviteData || inviteData.length === 0) {
|
||||
res.status(400).send('Bad or Expired Invite Code');
|
||||
return;
|
||||
}
|
||||
const invite = inviteData[0];
|
||||
if (invite.expiresAt && new Date(invite.expiresAt) < new Date()) {
|
||||
res.status(400).send('Invite Code Expired');
|
||||
return;
|
||||
}
|
||||
if (invite.uses >= invite.maxUses) {
|
||||
res.status(400).send('Invite Code Max Uses Reached');
|
||||
return;
|
||||
}
|
||||
res.render('admin/register', { inviteCode });
|
||||
});
|
||||
|
||||
app.post("/admin/register/:inviteCode", async (req, res) => {
|
||||
const inviteCode = req.params.inviteCode;
|
||||
if (!inviteCode) {
|
||||
res.render('admin/register', { error: 'Bad Request' });
|
||||
return;
|
||||
}
|
||||
const inviteData = await pool.query("SELECT * FROM admin_invites WHERE code = ?", [inviteCode]);
|
||||
if (!inviteData || inviteData.length === 0) {
|
||||
res.render('admin/register', { error: 'Bad or Expired Invite Code' });
|
||||
return;
|
||||
}
|
||||
const invite = inviteData[0];
|
||||
if (invite.expiresAt && new Date(invite.expiresAt) < new Date()) {
|
||||
res.render('admin/register', { error: 'Invite Code Expired' });
|
||||
return;
|
||||
}
|
||||
if (invite.uses >= invite.maxUses) {
|
||||
res.render('admin/register', { error: 'Invite Code Max Uses Reached' });
|
||||
return;
|
||||
}
|
||||
const username = req.body.username;
|
||||
const password = req.body.password;
|
||||
if (!username || !password) {
|
||||
res.render('admin/register', { error: 'Username and Password are required', inviteCode });
|
||||
return;
|
||||
}
|
||||
const existingUser = await pool.query("SELECT * FROM users WHERE username = ?", [String(username)]);
|
||||
if (existingUser && existingUser.length > 0) {
|
||||
res.render('admin/register', { error: 'Username already exists', inviteCode });
|
||||
return;
|
||||
}
|
||||
bcrypt.hash(password, saltRounds).then((hash) => {
|
||||
const newUser = pool.query("INSERT INTO users (username, passwordHash) VALUES (?, ?)",
|
||||
[String(username), hash]);
|
||||
const updateInvite = pool.query("UPDATE admin_invites SET uses = uses + 1 WHERE code = ?", [inviteCode]);
|
||||
Promise.all([newUser, updateInvite]).then(() => {
|
||||
res.redirect('/admin/login');
|
||||
}).catch(err => {
|
||||
console.error('Error creating user:', err);
|
||||
res.render('admin/register', { error: 'Internal server error', inviteCode });
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error hashing password:', err);
|
||||
res.render('admin/register', { error: 'Internal server error', inviteCode });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/api/v1/admin/routes', (req, res) => { // Get all routes
|
||||
if (!req.session.adminAuthenticated) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
|
|
9
migrations/009_add_admin_invites_table.sql
Normal file
9
migrations/009_add_admin_invites_table.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE IF NOT EXISTS admin_invites (
|
||||
code VARCHAR(36) PRIMARY KEY NOT NULL DEFAULT (UUID()),
|
||||
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
maxUses INTEGER NOT NULL DEFAULT 1,
|
||||
uses INTEGER NOT NULL DEFAULT 0,
|
||||
expiresAt TIMESTAMP,
|
||||
createdBy INTEGER,
|
||||
FOREIGN KEY (createdBy) REFERENCES users(id) ON DELETE SET NULL
|
||||
);
|
54
views/admin/register.ejs
Normal file
54
views/admin/register.ejs
Normal file
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
|
||||
<title>AstroCom Admin Registration</title>
|
||||
</head>
|
||||
|
||||
<body class="bg-dark">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center mt-5">
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card bg-dark text-light shadow">
|
||||
<div class="card-body p-4">
|
||||
<h2 class="text-center mb-4">Admin Registration</h2>
|
||||
<% if (typeof notice !== 'undefined') { %>
|
||||
<div class="alert alert-info text-center mb-3"><%= notice %></div>
|
||||
<% } %>
|
||||
<% if (typeof info !== 'undefined') { %>
|
||||
<div class="alert alert-primary text-center mb-3"><%= info %></div>
|
||||
<% } %>
|
||||
<% if (typeof warn !== 'undefined') { %>
|
||||
<div class="alert alert-warning text-center mb-3"><%= warn %></div>
|
||||
<% } %>
|
||||
<% if (typeof error !== 'undefined') { %>
|
||||
<div class="alert alert-danger text-center mb-3"><%= error %></div>
|
||||
<% } %>
|
||||
<form action="." method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username:</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password:</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Register</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer" class="text-light mt-5"></div>
|
||||
</div>
|
||||
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/assets/js/jquery.min.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
$("#footer").load("/footer");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue