password change feature
All checks were successful
Build Images and Deploy / Update-PROD-Stack (push) Successful in 29s

This commit is contained in:
2026-02-28 01:53:54 -05:00
parent 83e552bd07
commit b6cd483401
4 changed files with 64 additions and 0 deletions

10
public/favicon.svg Normal file
View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<circle cx="32" cy="32" r="30" fill="#6c5ce7"/>
<circle cx="32" cy="32" r="20" fill="none" stroke="#fdcb6e" stroke-width="3"/>
<circle cx="32" cy="32" r="10" fill="none" stroke="#fdcb6e" stroke-width="3"/>
<circle cx="32" cy="32" r="3" fill="#fdcb6e"/>
<line x1="32" y1="2" x2="32" y2="16" stroke="#fdcb6e" stroke-width="3" stroke-linecap="round"/>
<line x1="32" y1="48" x2="32" y2="62" stroke="#fdcb6e" stroke-width="3" stroke-linecap="round"/>
<line x1="2" y1="32" x2="16" y2="32" stroke="#fdcb6e" stroke-width="3" stroke-linecap="round"/>
<line x1="48" y1="32" x2="62" y2="32" stroke="#fdcb6e" stroke-width="3" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 723 B

View File

@@ -1,5 +1,6 @@
const express = require('express');
const router = express.Router();
const { requireAuth } = require('../middleware/auth');
const { Hunts, Packages, Scans, Users } = require('../models');
// ─── Hunt profile ─────────────────────────────────────────
@@ -95,6 +96,36 @@ router.get('/player/:username', (req, res) => {
});
});
// ─── Change password (own profile) ────────────────────────
router.post('/player/:username/password', requireAuth, (req, res) => {
const user = Users.findByUsername(req.params.username);
if (!user || user.id !== req.session.userId) {
return res.status(403).render('error', { title: 'Forbidden', message: 'You can only change your own password.' });
}
const { current_password, new_password, new_password_confirm } = req.body;
const fullUser = Users.findByUsername(user.username);
if (!Users.verifyPassword(fullUser, current_password)) {
req.session.flash = { type: 'danger', message: 'Current password is incorrect.' };
return res.redirect(`/player/${user.username}`);
}
if (!new_password || new_password.length < 6) {
req.session.flash = { type: 'danger', message: 'New password must be at least 6 characters.' };
return res.redirect(`/player/${user.username}`);
}
if (new_password !== new_password_confirm) {
req.session.flash = { type: 'danger', message: 'New passwords do not match.' };
return res.redirect(`/player/${user.username}`);
}
Users.setPassword(user.id, new_password);
req.session.flash = { type: 'success', message: 'Password changed successfully.' };
res.redirect(`/player/${user.username}`);
});
// ─── Browse all hunts ─────────────────────────────────────
router.get('/hunts', (req, res) => {
const hunts = Hunts.getAll();

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= typeof title !== 'undefined' ? title + ' | Loot Hunt' : 'Loot Hunt' %></title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="stylesheet" href="/css/style.css">
<script>
// Apply theme before render to prevent flash

View File

@@ -86,6 +86,28 @@
<div style="text-align: center; margin-top: 1rem;">
<a href="/leaderboard" class="btn btn-outline">Global Leaderboard</a>
</div>
<% if (typeof isOwnProfile !== 'undefined' && isOwnProfile) { %>
<div class="card" style="margin-top: 1.5rem;">
<div class="card-header">&#x1F512; Change Password</div>
<form method="POST" action="/player/<%= profile.username %>/password">
<div class="form-group">
<label for="current_password">Current Password</label>
<input type="password" id="current_password" name="current_password" class="form-control" required>
</div>
<div class="form-group">
<label for="new_password">New Password</label>
<input type="password" id="new_password" name="new_password" class="form-control" required minlength="6">
<div class="form-hint">At least 6 characters.</div>
</div>
<div class="form-group">
<label for="new_password_confirm">Confirm New Password</label>
<input type="password" id="new_password_confirm" name="new_password_confirm" class="form-control" required minlength="6">
</div>
<button type="submit" class="btn btn-primary btn-sm">Change Password</button>
</form>
</div>
<% } %>
</div>
<%- include('../partials/footer') %>