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
|
app.get('/api/v1/admin/routes', (req, res) => { // Get all routes
|
||||||
if (!req.session.adminAuthenticated) {
|
if (!req.session.adminAuthenticated) {
|
||||||
res.status(401).json({ error: 'Unauthorized' });
|
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