update to prepare for mailu migration

This commit is contained in:
Aidan 2025-01-19 18:27:23 -05:00
parent e926d6d3b3
commit e31072ad6a
20 changed files with 51 additions and 546 deletions

View File

@ -1,10 +0,0 @@
DB_NAME=pontusmail
DB_USER=root
DB_PASSWORD=passwdhere
DB_HOST=127.0.0.1
DB_PORT=3306
SESSION_SECRET=secretkeyhere
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin
INTERNAL_PORT=3000
MC_API_KEY=mailcowapikeyhere

235
app.js
View File

@ -2,8 +2,6 @@ const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const fs = require('fs');
const session = require('express-session');
const { Sequelize, DataTypes } = require('sequelize');
const axios = require('axios');
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 1800 });
@ -16,81 +14,47 @@ app.set('views', path.join(__dirname, 'src'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true
}));
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, {
host: process.env.DB_HOST || '127.0.0.1', // pulls from .env or defaults to localhost
port: process.env.DB_PORT || 3306, // pulls from .env or defaults to 3306
dialect: 'mysql'
});
//async function fetchDomainData() {
// const cachedData = cache.get('domainData');
// if (cachedData) {
// return cachedData;
// }
//
// try {
// const response = await axios.get('https://user.p0ntus.com/api/v1/get/domain/all', {
// headers: {
// 'accept': 'application/json',
// 'X-API-Key': process.env.MC_API_KEY
// }
// });
// const domainData = response.data;
// cache.set('domainData', domainData);
// return domainData;
// } catch (error) {
// console.error('Error fetching domain data:', error);
// return [];
// }
//}
sequelize.authenticate()
.then(() => console.log('Database connected'))
.catch(err => console.log('Error: ' + err));
const Request = sequelize.define('Request', {
fullName: DataTypes.STRING,
email: DataTypes.STRING,
reason: DataTypes.TEXT,
telegram: DataTypes.STRING,
status: {
type: DataTypes.STRING,
defaultValue: 'Pending'
}
});
async function fetchDomainData() {
const cachedData = cache.get('domainData');
if (cachedData) {
return cachedData;
}
try {
const response = await axios.get('https://user.p0ntus.com/api/v1/get/domain/all', {
headers: {
'accept': 'application/json',
'X-API-Key': process.env.MC_API_KEY
}
});
const domainData = response.data;
cache.set('domainData', domainData);
return domainData;
} catch (error) {
console.error('Error fetching domain data:', error);
return [];
}
}
function getDomains() {
const domainsPath = path.join(__dirname, 'domains.txt');
try {
const domains = fs.readFileSync(domainsPath, 'utf-8').split('\n').filter(Boolean);
return domains;
} catch (error) {
console.error('Error reading domains.txt:', error);
return [];
}
}
// Sync DB models
sequelize.sync();
//function getDomains() {
// const domainsPath = path.join(__dirname, 'domains.txt');
// try {
// const domains = fs.readFileSync(domainsPath, 'utf-8').split('\n').filter(Boolean);
// return domains;
// } catch (error) {
// console.error('Error reading domains.txt:', error);
// return [];
// }
//}
app.get('/', async (req, res) => {
const domainData = await fetchDomainData();
const domainCount = Array.isArray(domainData) ? domainData.length : 0;
const accountCount = Array.isArray(domainData) ? domainData.reduce((acc, domain) => acc + domain.mboxes_in_domain, 0) : 0;
const totalData = Array.isArray(domainData) ? domainData.reduce((acc, domain) => acc + parseInt(domain.bytes_total), 0) / (1024 * 1024) : 0;
//const domainData = await fetchDomainData();
//const domainCount = Array.isArray(domainData) ? domainData.length : 0;
//const accountCount = Array.isArray(domainData) ? domainData.reduce((acc, domain) => acc + domain.mboxes_in_domain, 0) : 0;
//const totalData = Array.isArray(domainData) ? domainData.reduce((acc, domain) => acc + parseInt(domain.bytes_total), 0) / (1024 * 1024) : 0;
res.render('index', {
currentPage: 'home',
domainCount,
accountCount,
totalData: totalData.toFixed(2) // Round to 2 decimal places
});
res.render('index', { currentPage: 'home' });
});
app.get('/services', (req, res) => {
@ -142,133 +106,6 @@ app.get('/guides/vaultwarden/firefox', (req, res) => {
res.render('guides/vaultwarden/firefox', { currentPage: 'guides' });
});
app.get('/register', (req, res) => {
const domains = getDomains();
res.render('register', { domains });
});
app.post('/register', async (req, res) => {
const { fullName, email, domain, reason, telegram } = req.body;
const crit = /^[a-zA-Z0-9.-]+$/; // regex (see also: public/js/register.js)
if (!crit.test(email) || /\s/.test(email) || email !== email.toLowerCase()) {
return res.render('error/500');
}
const fullEmail = `${email}@${domain}`;
try {
await Request.create({ fullName, email: fullEmail, reason, telegram });
res.render('reg-success', { currentPage: 'register' });
} catch (error) {
console.error('Error creating request:', error);
res.render('error/500');
}
});
app.get('/request', async (req, res) => {
console.log("Found!");
const { email } = req.query;
const domains = getDomains();
if (!email) {
return res.render('error/email', { domains });
}
try {
const request = await Request.findOne({ where: { email } });
if (!request) {
return res.status(404).render('error/notfoundemail', { email, domain });
}
res.render('request', { request, domains });
} catch (error) {
console.error(error);
res.render('error/500');
}
});
app.post('/request', async (req, res) => {
console.log("Found!");
const { email, domain } = req.body;
const fullEmail = `${email}@${domain}`;
const domains = getDomains();
if (!email || !domain) {
return res.render('error/email', { domains });
}
try {
const request = await Request.findOne({ where: { email: fullEmail } });
const domains = getDomains();
if (!request) {
return res.render('error/notfoundemail', { email, domain });
}
res.render('request', { request, domains });
} catch (error) {
console.error(error);
res.render('error/500');
}
});
function checkAdminAuth(req, res, next) {
if (req.session.admin) {
next();
} else {
res.redirect('/admin');
}
}
// Admin routes
app.get('/admin', (req, res) => {
if (req.session.admin) {
return res.redirect('/admin/dashboard');
}
res.render('admin/login', { currentPage: 'admin', error: null });
});
app.post('/admin', (req, res) => {
const { username, password } = req.body;
if (username === process.env.ADMIN_USERNAME && password === process.env.ADMIN_PASSWORD) {
req.session.admin = true;
res.redirect('/admin/dashboard');
} else {
res.render('admin/login', { error: 'An error occurred.' });
}
});
app.get('/admin/dashboard', checkAdminAuth, async (req, res) => {
const requests = await Request.findAll();
res.render('admin/dash', { requests, currentPage: 'admin', user: process.env.ADMIN_USERNAME });
});
app.post('/admin/update-status', checkAdminAuth, async (req, res) => {
const { id, status } = req.body;
await Request.update({ status }, { where: { id } });
res.redirect('/admin/dashboard');
});
app.post('/admin/delete-request', checkAdminAuth, async (req, res) => {
const { id } = req.body;
await Request.destroy({ where: { id } });
res.redirect('/admin/dashboard');
});
app.get('/admin/edit/:id', checkAdminAuth, async (req, res) => {
const { id } = req.params;
const request = await Request.findByPk(id);
if (!request) {
return res.status(404).render('error/404');
}
res.render('admin/edit', { request, currentPage: 'admin' });
});
app.post('/admin/edit', checkAdminAuth, async (req, res) => {
const { id, fullName, email, reason, telegram } = req.body;
await Request.update({ fullName, email, reason, telegram }, { where: { id } });
res.redirect('/admin/dashboard');
});
// Start server on internal port defined in .env
app.listen(process.env.INTERNAL_PORT, () => {
console.log(`Server started on port ${process.env.INTERNAL_PORT}`);

BIN
bun.lockb Executable file

Binary file not shown.

View File

@ -17,7 +17,7 @@
"express": "^4.21.1",
"express-session": "^1.18.1",
"mariadb": "^3.4.0",
"mysql2": "^3.11.5",
"mysql2": "^3.12.0",
"node-cache": "^5.1.2",
"sequelize": "^6.37.5",
"winston": "^3.17.0"

View File

@ -1,58 +0,0 @@
<%- include('shards/header', { title: 'Admin Dashboard - p0ntus mail' }) %>
<div class="container my-5">
<div class="text-center mb-4">
<h1 class="fw-bold">Welcome, <%= user %>!</h1>
<p>You are viewing <b>all requests</b></p>
</div>
<div class="table-responsive">
<table class="table align-middle table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Full Name</th>
<th>Email</th>
<th>Reason</th>
<th>Telegram</th>
<th>Status</th>
<th>Update Status</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<% requests.forEach(request => { %>
<tr>
<td><%= request.id %></td>
<td><%= request.fullName %></td>
<td><%= request.email %></td>
<td><%= request.reason %></td>
<td><%= request.telegram %></td>
<td><%= request.status %></td>
<td>
<form action="/admin/update-status" method="POST" class="d-inline">
<input type="hidden" name="id" value="<%= request.id %>">
<select name="status" class="form-select form-select-sm">
<option value="Pending" <% if (request.status === 'Pending') { %>selected<% } %>>Pending</option>
<option value="Approved" <% if (request.status === 'Approved') { %>selected<% } %>>Approved</option>
<option value="Denied" <% if (request.status === 'Denied') { %>selected<% } %>>Denied</option>
</select>
<button type="submit" class="btn btn-primary btn-sm mt-2">Update</button>
</form>
</td>
<td>
<a href="/admin/edit/<%= request.id %>" class="btn btn-warning btn-sm">
<i class="bi bi-pencil"></i>
</a>
<form action="/admin/delete-request" method="POST" class="d-inline">
<input type="hidden" name="id" value="<%= request.id %>">
<button type="submit" class="btn btn-danger btn-sm">
<i class="bi bi-trash3"></i>
</button>
</form>
</td>
</tr>
<% }); %>
</tbody>
</table>
</div>
</div>
<%- include('shards/footer') %>

View File

@ -1,25 +0,0 @@
<%- include('shards/header', { title: 'Edit Request - p0ntus mail' }) %>
<div class="container my-5">
<h2 class="mb-4">Edit Request</h2>
<form action="/admin/edit" method="POST">
<input type="hidden" name="id" value="<%= request.id %>">
<div class="mb-3">
<label for="fullName" class="form-label">Full Name</label>
<input type="text" class="form-control" id="fullName" name="fullName" value="<%= request.fullName %>" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="text" class="form-control" id="email" name="email" value="<%= request.email %>" required>
</div>
<div class="mb-3">
<label for="reason" class="form-label">Reason</label>
<textarea class="form-control" id="reason" name="reason" rows="3"><%= request.reason %></textarea>
</div>
<div class="mb-3">
<label for="telegram" class="form-label">Telegram</label>
<input type="text" class="form-control" id="telegram" name="telegram" value="<%= request.telegram %>">
</div>
<button type="submit" class="btn btn-primary">Update</button>
</form>
</div>
<%- include('shards/footer') %>

View File

@ -1,21 +0,0 @@
<%- include('../shards/header', { title: 'Admin - p0ntus mail' }) %>
<div class="container">
<%- include('../shards/nav', { currentPage: 'admin' }) %>
<i class="il mt-5">Login to administration panel</i>
<hr>
<% if (error) { %>
<div class="alert alert-danger"><%= error %></div>
<% } %>
<form action="/admin" method="POST">
<div class="form-group mt-2">
<label for="username">Username</label>
<input type="text" class="form-control mt-1" id="username" name="username" required>
</div>
<div class="form-group mt-2">
<label for="password">Password</label>
<input type="password" class="form-control mt-1" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-dark mt-3">Login</button>
</form>
</div>
<%- include('../shards/footer') %>

View File

@ -1,4 +0,0 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="/js/admin.js"></script>
</body>
</html>

View File

@ -1,37 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><%= title %></title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css" integrity="sha512-dPXYcDub/aeb08c63jRq/k6GaKccl256JQy/AnOq7CAnEZ9FzSL9wSbcZkMp4R26vBsMLFYH4kQ67/bbV8XaCQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
:root {
--bg-color-light: #f9f9f9;
--text-color-light: #333;
--bg-color-dark: #1c1c1c;
--text-color-dark: #fff;
}
body.light-mode {
background-color: var(--bg-color-light);
color: var(--text-color-light);
}
body.dark-mode {
background-color: var(--bg-color-dark);
color: var(--text-color-dark);
}
.mode-switch {
cursor: pointer;
margin-left: 1rem;
}
</style>
</head>
<body class="light-mode">
<nav id="nav" class="navbar navbar-expand-lg navbar-light bg-light px-4">
<a class="navbar-brand" href="#">p0ntus Mail Admin</a>
<div class="ms-auto">
<span class="mode-switch" id="modeSwitch">
<i id="iconSwitch" class="bi-moon text-black"></i>
</span>
</div>
</nav>

View File

@ -1,7 +0,0 @@
<%- include('../shards/header', { title: '404 Not Found - p0ntus mail', currentPage: 'err' }) %>
<div class="container">
<i class="il mt-2">404 Not Found</i>
<hr>
<p>The requested resource could not be found.</p>
</div>
<%- include('../shards/footer') %>

View File

@ -1,7 +0,0 @@
<%- include('../shards/header', { title: '500 Internal Server Error - p0ntus mail', currentPage: 'err' }) %>
<div class="container">
<i class="il mt-2">500 Internal Server Error</i>
<hr>
<p>An unexpected error occurred. Please try again later.</p>
</div>
<%- include('../shards/footer') %>

View File

@ -1,25 +0,0 @@
<%- include('../shards/header', { title: 'Request Status - p0ntus mail', currentPage: 'err' }) %>
<div class="container">
<%- include('../shards/nav', { currentPage: 'request' }) %>
<i class="il mt-5">We need your email</i>
<hr>
<p class="mt-4 mb-4">We need an email to check your request. Please enter your p0ntus mail email address below:</p>
<form action="/request" method="post" onsubmit="return validateEmail()">
<div class="form-group">
<label class="mb-2" for="email">p0ntus mail email address:</label>
<div class="input-group">
<input type="text" class="form-control" id="email" name="email" pattern="^[a-zA-Z0-9.\-]+$" required oninput="this.value = this.value.toLowerCase().replace(/\s/g, '')">
<div class="input-group-prepend">
<span class="input-group-text">@</span>
</div>
<select class="form-select" id="domain" name="domain" required>
<% domains.forEach(domain => { %>
<option value="<%= domain %>"><%= domain %></option>
<% }); %>
</select>
</div>
</div>
<button type="submit" class="btn btn-dark mt-3">Submit</button>
</form>
</div>
<%- include('../shards/footer') %>

View File

@ -1,7 +0,0 @@
<%- include('../shards/header', { title: "Couldn't find that email - p0ntus mail", currentPage: 'err' }) %>
<div class="container">
<i class="il mt-2">We couldn't find that email</i>
<hr>
<p>We couldn't find a request under <b><%= email %>@<%= domain %></b>. Please try again later or try and register again.</p>
</div>
<%- include('../shards/footer') %>

View File

@ -10,33 +10,20 @@
<div class="row">
<div class="col-md-6 text-start">
<h5><i>If you aren't a current user</i></h5>
<a href="/register" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-user-plus ico-sm"></i> Register</a>
<a href="/request" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-person-circle-question ico-sm"></i> Check Registration Status</a>
<a href="https://user.pontusmail.org/admin/user/signup" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-user-plus ico-sm"></i> Register</a>
</div>
<div class="col-md-6 text-start">
<h5><i>If you are a current user</i></h5>
<a href="/SOGo" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-envelope ico-sm"></i> Webmail</a>
<a href="https://user.p0ntus.com" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-user ico-sm"></i> Account</a>
<a href="https://user.pontusmail.org/webmail/" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-envelope ico-sm"></i> Webmail</a>
<a href="https://user.pontusmail.org" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-user ico-sm"></i> Account</a>
<a href="https://vaultwarden.p0ntus.com" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-key ico-sm"></i> Vaultwarden</a>
<a href="/guides" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-book ico-sm"></i> Guides</a>
<a href="https://user.pontusmail.org/admin/client" class="btn bg-dark text-white mt-2"><i class="fa-solid fa-question-mark ico-sm"></i> Client Setup</a>
</div>
</div>
<i class="il mt-5">Statistics</i>
<hr>
<div class="row mt-5">
<div class="col-md-4 text-center">
<h3><%= domainCount %></h3>
<p>Domains Hosted</p>
</div>
<div class="col-md-4 text-center">
<h3><%= accountCount %></h3>
<p>Accounts</p>
</div>
<div class="col-md-4 text-center">
<h3><%= totalData %></h3>
<p>Total Data (MB)</p>
</div>
</div>
<p>As we have migrated to a new email platform, we are currently rewriting this.</p>
<small class="form-text text-muted mt-2">Please note all domains may not be available for registration.</small>
</div>
<%- include('shards/footer') %>

View File

@ -5,17 +5,15 @@
<hr>
<p class="text-start"><b>Our system collects automatically:</b></p>
<ul class="list-group text-start">
<li class="list-group-item">Log files which may include the currently logged-in IP address. Log files are stored in memory, and Redis lists.</li>
<li class="list-group-item">Last login time</li>
<li class="list-group-item">Storage used by your account</li>
<li class="list-group-item">Message count for your account</li>
<li class="list-group-item">Last message received time</li>
<li class="list-group-item">Account creation date</li>
<li class="list-group-item">Log files which may include the currently logged-in IP address, user agent, or other identifying information. We clear this data as much as possible.</li>
<li class="list-group-item">Storage and disk quota used by your account</li>
<li class="list-group-item">Account creation date (no time)</li>
<li class="list-group-item">Linked external email addresses' folders, last check date, status, created date, users, and the ability to take actions (like you can on your dashboard).</li>
<li class="list-group-item">Automatic reply settings</li>
</ul>
<p class="text-start mt-3"><b>What I collect by choice:</b></p>
<ul class="list-group text-start">
<li class="list-group-item">Email used at registration</li>
<li class="list-group-item">Telegram account (if given at registration)</li>
<li class="list-group-item">I do not collect any extra information at the moment</li>
</ul>
<p class="text-start mt-3"><b>What the admin has access to:</b></p>
<ul class="list-group text-start">

View File

@ -1,8 +0,0 @@
<%- include('shards/header', { title: 'Registration Successful - p0ntus mail' }) %>
<div class="container">
<%- include('shards/nav', { currentPage: 'register' }) %>
<i class="il mt-5">Registration Submitted</i>
<hr>
<p>Your request has been submitted successfully. You can check the status of your request <a href="/request">here</a>.</p>
</div>
<%- include('shards/footer') %>

View File

@ -1,46 +0,0 @@
<%- include('shards/header', { title: 'Register - p0ntus mail', currentPage: 'register' }) %>
<div class="container">
<%- include('shards/nav', { currentPage: 'register' }) %>
<h1 class="mt-5">Register</h1>
<i class="il mt-5">If you already filled out a registration request</i>
<hr>
<div class="text-center">
<a href="/request" class="btn bg-dark text-white mt-2">
<i class="fa-solid fa-person-circle-question ico-sm"></i> Check Registration Status
</a>
</div>
<i class="il mt-5">If you haven't filled out a registration request</i>
<hr>
<p class="mt-3"><b>Do not use this service for spam. It is a waste of your time.</b> I have added protections to prevent spam, including sending quotas per hour. I will catch you before you can send a good enough amount.</p>
<form action="/register" method="POST">
<div class="form-group mb-3">
<label for="fullName" class="mb-2">Name that goes in email header (full name/username/alias)</label>
<input type="text" class="form-control" id="fullName" name="fullName" required>
</div>
<div class="form-group mb-3">
<label for="email" class="mb-2">Desired email address (e.g., yourname@p0ntus.com) - <b>Do not enter your current email in this box</b></label>
<div class="input-group">
<input type="text" class="form-control" id="email" name="email" pattern="^[a-zA-Z0-9.-]+$" required oninput="this.value = this.value.toLowerCase().replace(/\s/g, '')">
<div class="input-group-prepend">
<span class="input-group-text">@</span>
</div>
<select class="form-select" id="domain" name="domain" required>
<% domains.forEach(domain => { %>
<option value="<%= domain %>"><%= domain %></option>
<% }); %>
</select>
</div>
<small class="form-text text-muted">Only letters, numbers, periods, and dashes are allowed.</small>
</div>
<div class="form-group mb-3">
<label for="reason" class="mb-2">Reason for wanting an email (at least one full sentence)</label>
<textarea class="form-control" id="reason" name="reason" rows="3" required></textarea>
</div>
<div class="form-group mb-3">
<label for="telegram" class="mb-2">Telegram username</label>
<input type="text" class="form-control" id="telegram" name="telegram" value="@" required>
</div>
<button type="submit" class="btn btn-dark mt-3"><i class="fa-solid fa-user-plus ico-sm"></i> Register</button>
</form>
</div>
<%- include('shards/footer') %>

View File

@ -1,51 +0,0 @@
<%- include('shards/header', { title: 'Request Status - p0ntus mail', currentPage: 'request' }) %>
<div class="container">
<%- include('shards/nav', { currentPage: 'request' }) %>
<i class="il mt-5">Request Status</i>
<hr>
<form action="/request" method="POST">
<div class="form-group mb-3">
<label for="email" class="mb-2">Email</label>
<div class="input-group">
<input type="text" class="form-control" id="email" name="email" pattern="^[a-zA-Z0-9.-]+$" required oninput="this.value = this.value.toLowerCase().replace(/\s/g, '')">
<div class="input-group-prepend">
<span class="input-group-text">@</span>
</div>
<select class="form-select" id="domain" name="domain" required>
<% domains.forEach(domain => { %>
<option value="<%= domain %>"><%= domain %></option>
<% }); %>
</select>
</div>
<small class="form-text text-muted">Only letters, numbers, periods, and dashes are allowed.</small>
</div>
<button type="submit" class="btn btn-dark mt-3"><i class="fa-solid fa-search ico-sm"></i> Check Status</button>
</form>
<% if (request) { %>
<hr class="mt-4" />
<p>Email: <strong><%= request.email %></strong></p>
<p>Request Status: <strong><%= request.status %></strong></p>
<% if (request.status === 'Approved') { %>
<div class="text-start">
<p>Your request has been approved. You can access your p0ntus mail account <a href="https://user.p0ntus.com/">here</a> with your email as the username and abcabc for the password. <b>FOR SECURITY, CHANGE YOUR PASSWORD ASAP.</b></p>
<p>I also suggest you check out our guides at <a href="https://mail.p0ntus.com/guides">mail.p0ntus.com/guides</a>.</p>
<p>You can also access Vaultwarden at <a href="https://vaultwarden.p0ntus.com/">vaultwarden.p0ntus.com</a>.</p>
<p>If you need any help or support, contact <a href="mailto:admin@p0ntus.com">admin@p0ntus.com</a>.</p>
<p>Your webmail can be accessed <a href="https://mail.p0ntus.com/SOGo">here</a>.</p>
</div>
<% } %>
<% if (request.status === 'Pending') { %>
<p>Your request is pending approval. This page will be updated when your request been approved or denied.</p>
<% } %>
<% if (request.status === 'Denied') { %>
<p>Your request has been denied.</p>
<p>If you believe this is an error, please contact <a href="mailto:admin@p0ntus.com">admin@p0ntus.com</a>.</p>
<p>Please ensure the username you selected only includes letters, numbers, and periods/dashes. Your request may also be declined if you do not provide a valid reason or response in your application.</p>
<p>You may resubmit another request if you feel you deserve an email, or have fixed issues with your application.</p>
<% } %>
<% } else { %>
<p>No request found for the provided email.</p>
<% } %>
</div>
<%- include('shards/footer') %>

View File

@ -44,7 +44,8 @@
<p>Upon creating your account, you will be allocated an account with the following:</p>
<ul class="list-group text-start">
<li class="list-group-item">4000 MB storage (may vary)</li>
<li class="list-group-item">Webmail</li>
<li class="list-group-item">Roundcube Webmail</li>
<li class="list-group-item">Contacts List</li>
<li class="list-group-item">IMAP/SMTP access</li>
<li class="list-group-item">Spam protection</li>
<li class="list-group-item">5 outgoing messages per hour</li>

View File

@ -12,11 +12,7 @@
<a href="/services" class="nav-link text-dark">Services</a>
<% } %>
<a href="https://status.p0ntus.com/" class="nav-link text-dark">Status</a>
<% if (currentPage === 'register') { %>
<span class="nav-link text-dark"><b>Register</b></span>
<% } else { %>
<a href="/register" class="nav-link text-dark">Register</a>
<% } %>
<a href="https://user.pontusmail.org/admin/user/signup" class="nav-link text-dark">Register</a>
<% if (currentPage === 'donate') { %>
<span class="nav-link text-dark"><b>Donate</b></span>
<% } else { %>
@ -32,12 +28,4 @@
<% } else { %>
<a href="/guides" class="nav-link text-dark">Guides</a>
<% } %>
<% if (currentPage === 'request') { %>
<span class="nav-link text-dark"><b>My Request</b></span>
<% } else { %>
<a href="/request" class="nav-link text-dark">My Request</a>
<% } %>
<% if (currentPage === 'admin') { %>
<span class="nav-link text-dark"><b>Admin</b></span>
<% } %>
</nav>