diff options
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | index.js | 63 | ||||
| -rw-r--r-- | package.json | 7 | ||||
| -rw-r--r-- | public/css/style.css | 17 | ||||
| -rw-r--r-- | status.json | 37 | ||||
| -rw-r--r-- | views/dashboard.ejs | 42 | ||||
| -rw-r--r-- | views/error.ejs | 4 | ||||
| -rw-r--r-- | views/index.ejs | 6 | ||||
| -rw-r--r-- | views/password.ejs | 22 | ||||
| -rw-r--r-- | views/register.ejs | 5 | ||||
| -rw-r--r-- | views/success.ejs | 7 |
12 files changed, 160 insertions, 58 deletions
@@ -174,4 +174,6 @@ dist # Finder (MacOS) folder config .DS_Store -*.db
\ No newline at end of file +*.db + +.idea/
\ No newline at end of file @@ -1,5 +1,5 @@ # bnbso-auth -Web authentication for bnbSO (registering accounts, resetting passwords, changing passwords) using Discord authentication. Designed for FreeSO-based servers. +Web authentication for bnbSO (registering accounts, changing passwords) using Discord authentication. Designed for FreeSO-based servers. To install dependencies: @@ -10,7 +10,7 @@ bun install To run: ```bash -bun run index.ts +bun run index.js ``` This project was created using `bun init` in bun v1.1.38. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. @@ -86,7 +86,7 @@ app.get("/", async (req, res) => { } }); } else { - return res.render('error', { error: 'You must be a member of the bits & Bytes server to access this page.' }); + return res.render('error', { error: 'You must be a member of that server to access this page.' }); } } else { res.render('index'); @@ -105,7 +105,7 @@ app.post("/register", upload.none(), async (req, res) => { try { const form = new FormData(); form.append('username', username); - form.append('email', id + '@discord.com'); + form.append('email', `${id}@discord.com`); form.append('password', password); form.append('key', process.env.REG_KEY); @@ -123,7 +123,7 @@ app.post("/register", upload.none(), async (req, res) => { console.error("Error inserting user data into database:", err); return res.render('register', { ...req.user, error: "An error occurred during registration, contact server operator." }); } - return res.render('success'); + return res.render('success', { ...req.user, success: "Created account successfully!"}); }); } } catch (error) { @@ -131,7 +131,62 @@ app.post("/register", upload.none(), async (req, res) => { return res.render('register', { ...req.user, error: "An error occurred during registration, contact server operator." }); } } else { - res.redirect("/"); + res.status(401).send("Unauthorized."); + } +}); + +app.get('/password', (req, res) => { + if (req.isAuthenticated()) { + res.render('password'); + } else { + res.redirect("/auth/discord"); + } +}); + +app.post('/password/change', upload.none(), async (req, res) => { + if (req.isAuthenticated()) { + const { id } = req.user; + const { currentpassword, newpassword, newpassword2 } = req.body; + + if (newpassword !== newpassword2) { + return res.render('password', { ...req.user, error: "Passwords do not match" }); + } + + try { + db.get(`SELECT * FROM users WHERE discord_id = ?`, [id], async (err, row) => { + if (err) { + console.error("Error querying the database:", err); + return res.render('password', {...req.user, error: "An error occurred while checking user data."}); + } + + if (row) { + const form = new FormData(); + form.append('username', row.fso_username); + form.append('old_password', currentpassword); + form.append('new_password', newpassword); + + const response = await axios.post(`${process.env.API_URL}/userapi/password`, form, { + headers: form.getHeaders() + }); + + if (response.data.error) { + const errorKey = response.data.error_description || "default"; + const errorMessage = statusMessages.password_reset_errors[errorKey] || "Something went wrong"; + + return res.render('password', { ...req.user, error: errorMessage }); + } + + return res.render('success', { ...req.user, success: "Password changed successfully!" }); + } + }); + + } catch (error) { + console.error("Error during password change:", error); + return res.render('password', { ...req.user, error: "An error occurred during password change, contact server operator." }); + } + + } else { + res.status(401).send("Unauthorized."); } }); diff --git a/package.json b/package.json index 10776ca..ed9fd67 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,10 @@ { "name": "bnbso-auth", - "module": "index.ts", + "module": "index.js", "type": "module", + "scripts": { + "start": "node index.js" + }, "devDependencies": { "@types/bun": "latest" }, @@ -20,4 +23,4 @@ "passport-discord": "^0.1.4", "sqlite3": "^5.1.7" } -}
\ No newline at end of file +} diff --git a/public/css/style.css b/public/css/style.css index a6c3ea8..1472a8c 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,10 +1,10 @@ @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap'); - + html, body { height: 100%; margin: 0; padding: 0; -} +} body { display: flex; @@ -34,7 +34,7 @@ a:active { display: inline-block; text-decoration: none; color: #fff; - background-color: #00633a; + background-color: #535353; padding: 10px 20px; border-radius: 5px; transition: 0.2s; @@ -43,12 +43,12 @@ a:active { } .button:hover { - background-color: #009959; + background-color: #8a8a8a; color: #fff; } .button:active { - background-color: #003620; + background-color: #454545; color: #fff; } @@ -139,4 +139,9 @@ button { .error { color: #f88c8c; font-size: 1.5em; -}
\ No newline at end of file +} + +.success { + color: #a3f88c; + font-size: 1.5em; +} diff --git a/status.json b/status.json index eb1f9e9..076dd3a 100644 --- a/status.json +++ b/status.json @@ -1,29 +1,28 @@ { "registration_errors": { - "missing_confirmation_token": "Registration failed: Missing confirmation token.", - "user_short": "Registration failed: Username is too short.", - "user_long": "Registration failed: Username is too long.", - "user_invalid": "Registration failed: Invalid username.", - "pass_required": "Registration failed: Password is required.", - "email_invalid": "Registration failed: Invalid email address.", - "ip_banned": "Registration failed: IP is banned.", - "registrations_too_frequent": "Registration failed: Too many registrations from this IP address.", - "user_exists": "Registration failed: User already exists.", - "smtp_disabled": "Registration failed: SMTP service is disabled.", - "email_taken": "Registration failed: Email address is already taken.", - "confirmation_pending": "Registration failed: Confirmation pending.", - "key_wrong": "Registration failed: Invalid registration key." + "missing_confirmation_token": "Missing confirmation token.", + "user_short": "Username is too short.", + "user_long": "Username is too long.", + "user_invalid": "Invalid username.", + "pass_required": "Password is required.", + "email_invalid": "Invalid email address.", + "ip_banned": "IP is banned.", + "registrations_too_frequent": "Too many registrations from this IP address.", + "user_exists": "User already exists.", + "smtp_disabled": "SMTP service is disabled.", + "email_taken": "Email address is already taken.", + "confirmation_pending": "Confirmation pending.", + "key_wrong": "Invalid registration key." }, "password_reset_errors": { - "missing_fields": "Password reset failed: Missing required fields.", - "email_invalid": "Password reset failed: Invalid email address.", - "user_invalid": "Password reset failed: User does not exist.", - "incorrect_password": "Password reset failed: Incorrect password.", - "invalid_token": "Password reset failed: Invalid confirmation token." + "missing_fields": "Missing required fields.", + "email_invalid": "Invalid email address.", + "user_invalid": "User does not exist.", + "incorrect_password": "Incorrect password.", + "invalid_token": "Invalid confirmation token." }, "success_responses": { "success": "Operation was successful.", "email_failed": "Email sending failed." } } -
\ No newline at end of file diff --git a/views/dashboard.ejs b/views/dashboard.ejs index 1c7996b..b7c82d5 100644 --- a/views/dashboard.ejs +++ b/views/dashboard.ejs @@ -9,12 +9,44 @@ <body> <div class="background"></div> <div class="container"> - <img src="img/logo.png" alt="logo" width="200"> + <img src="/img/logo.png" alt="logo" width="200"> <h1>Welcome, <%= username %>!</h1> - <h2>FreeSO Username: <%= fso_username %></h2> - <a href="#" class="button">Change Password</a> - <a href="#" class="button">Download bnbSO Client</a> + <h2>bnbSO Username: <%= fso_username %></h2> + <h2 id="simtime"></h2> + <a href="/password" class="button">Change Password</a> + <a href="https://fso-builds.riperiperi.workers.dev" class="button">Download bnbSO Client</a> <a href="/logout" class="button logout">Logout</a> </div> + <script> + function updateTSOClock() { + const currentTime = new Date(), + utcMinutes = currentTime.getUTCMinutes(), + utcSeconds = currentTime.getUTCSeconds(); + let timePeriod = 'AM', totalSeconds = 0; + if (currentTime.getUTCHours() % 2 === 1) { + totalSeconds = 3600; + timePeriod = 'PM'; + } + totalSeconds += utcMinutes * 60 + utcSeconds; + let hour = Math.floor(totalSeconds / 300); + if (hour > 12) { + hour -= 12; + } + if (hour === 0) { + hour = 12; + } + let minute = Math.floor(totalSeconds % 300 / 5); + if (minute < 10) { + minute = '0' + minute; + } + const simTimeElement = document.querySelector('#simtime'); + if (simTimeElement) { + simTimeElement.textContent = `${hour}:${minute} ${timePeriod}`; + } + } + + setInterval(updateTSOClock, 1000); + updateTSOClock(); + </script> </body> -</html>
\ No newline at end of file +</html> diff --git a/views/error.ejs b/views/error.ejs index dc138e6..b867d30 100644 --- a/views/error.ejs +++ b/views/error.ejs @@ -9,9 +9,9 @@ <body> <div class="background"></div> <div class="container"> - <img src="img/logo.png" alt="logo" width="200"> + <img src="/img/logo.png" alt="logo" width="200"> <h1>Oh no! Something went wrong!</h1> <p><%= error %></p> </div> </body> -</html>
\ No newline at end of file +</html> diff --git a/views/index.ejs b/views/index.ejs index e786bd3..7a9b8a4 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -9,10 +9,10 @@ <body> <div class="background"></div> <div class="container"> - <img src="img/logo.png" alt="logo" width="200"> + <img src="/img/logo.png" alt="logo" width="200"> <p>Log into your Discord account to get access to bnbSO.</p> <p><i>You must be a bits & Bytes member.</i></p> - <a class="button discord" href="/auth/discord">Login with Discord</a> + <a class="button discord" href="/auth/discord">Login with Discord</a> </div> </body> -</html>
\ No newline at end of file +</html> diff --git a/views/password.ejs b/views/password.ejs index f66b60b..8033aae 100644 --- a/views/password.ejs +++ b/views/password.ejs @@ -9,18 +9,22 @@ <body> <div class="background"></div> <div class="container"> - <img src="img/logo.png" alt="logo" width="200"> + <img src="/img/logo.png" alt="logo" width="200"> <h1>Change Password</h1> - <p>If you have issues changing your password, ask the server operator to change your password.</p> - <form method="post" action="/login"> - <label for="username">Username:</label> - <input type="text" id="username" name="username"> - <label for="password">Current Password:</label> - <input type="password" id="password" name="password"> - <button type="submit">Login</button> + <p>If you have issues changing your password, ask the server operator to reset your password.</p> + <form method="post" action="/password/change"> + <label for="currentpassword">Current Password:</label> + <input type="password" id="currentpassword" name="currentpassword"> + <label for="newpassword">New Password:</label> + <input type="password" id="newpassword" name="newpassword"> + <label for="newpassword2">Confirm New Password:</label> + <input type="password" id="newpassword2" name="newpassword2"> + <button type="submit">Change Password</button> </form> + <a href="/" class="button">Dashboard</a> <% if (typeof error !== 'undefined') { %> <div class="error"><%= error %></div> <% } %> + </div> </body> -</html>
\ No newline at end of file +</html> diff --git a/views/register.ejs b/views/register.ejs index f417f82..d54013d 100644 --- a/views/register.ejs +++ b/views/register.ejs @@ -9,7 +9,7 @@ <body> <div class="background"></div> <div class="container"> - <img src="img/logo.png" alt="logo" width="200"> + <img src="/img/logo.png" alt="logo" width="200"> <h1>Welcome to bnbSO!</h1> <p>You will be sending the following information to register your bnbSO account</p> <p>Please verify that the following information is correct. You can only change your username <b>once</b>.</p> @@ -25,5 +25,6 @@ <% if (typeof error !== 'undefined') { %> <div class="error"><%= error %></div> <% } %> + </div> </body> -</html>
\ No newline at end of file +</html> diff --git a/views/success.ejs b/views/success.ejs index 2ec9771..f87ea2f 100644 --- a/views/success.ejs +++ b/views/success.ejs @@ -9,9 +9,10 @@ <body> <div class="background"></div> <div class="container"> - <img src="img/logo.png" alt="logo" width="200"> - <p>Created account successfully!</p> + <img src="/img/logo.png" alt="logo" width="200"> + <h1>Success!</h1> + <p class="success"><%= success %></p> <a href="/">Dashboard</a> </div> </body> -</html>
\ No newline at end of file +</html> |
