- move config requiredVariables to contants
All checks were successful
Code quality checks / biome (push) Successful in 13s

- change register email
- add verify route ( mostly still needs to be tested
This commit is contained in:
creations 2025-06-11 09:20:48 -04:00
parent 83b71f62cf
commit fff3c3ca50
Signed by: creations
GPG key ID: 8F553AA4320FC711
16 changed files with 482 additions and 89 deletions

View file

@ -1,6 +1,7 @@
import { echo } from "@atums/echo";
import { validateJWTConfig, validateMailerConfig } from "#lib/validation";
import { isValidUrl } from "#lib/validation/url";
import { requiredVariables } from "./constants";
import { cassandraConfig, validateCassandraConfig } from "./database/cassandra";
import { jwt } from "./jwt";
import { mailerConfig } from "./mailer";
@ -12,30 +13,11 @@ const environment: Environment = {
host: process.env.HOST || "0.0.0.0",
development:
process.env.NODE_ENV === "development" || process.argv.includes("--dev"),
fqdn: process.env.FRONTEND_FQDN || "",
frontendFqdn: process.env.FRONTEND_FQDN || "",
backendFqdn: process.env.BACKEND_FQDN || "",
};
function verifyRequiredVariables(): void {
const requiredVariables = [
"HOST",
"PORT",
"REDIS_URL",
"REDIS_TTL",
"CASSANDRA_HOST",
"CASSANDRA_PORT",
"CASSANDRA_CONTACT_POINTS",
"CASSANDRA_AUTH_ENABLED",
"CASSANDRA_DATACENTER",
"JWT_SECRET",
"JWT_EXPIRATION",
"JWT_ISSUER",
"FRONTEND_FQDN",
];
let hasError = false;
for (const key of requiredVariables) {
@ -56,9 +38,11 @@ function verifyRequiredVariables(): void {
}
const validateJWT = validateJWTConfig(jwt);
if (!validateJWT.valid) {
if (!validateJWT.isValid) {
echo.error("JWT configuration validation failed:");
echo.error(`- ${validateJWT.error}`);
for (const error of validateJWT.errors) {
echo.error(`- ${error}`);
}
hasError = true;
}
@ -71,19 +55,24 @@ function verifyRequiredVariables(): void {
hasError = true;
}
const urlValidation = isValidUrl(environment.fqdn, {
requireProtocol: true,
failOnTrailingSlash: true,
allowedProtocols: ["http", "https"],
allowLocalhost: environment.development,
allowIP: environment.development,
});
const validateUrl = (url: string, name: string) => {
const validation = isValidUrl(url, {
requireProtocol: true,
failOnTrailingSlash: true,
allowedProtocols: ["http", "https"],
allowLocalhost: environment.development,
allowIP: environment.development,
});
if (!urlValidation.valid) {
echo.error("FRONTEND_FQDN validation failed:");
echo.error(`- ${urlValidation.error}`);
hasError = true;
}
if (!validation.valid) {
echo.error(`${name} validation failed:`);
echo.error(`- ${validation.error}`);
hasError = true;
}
};
validateUrl(environment.frontendFqdn, "FRONTEND_FQDN");
validateUrl(environment.backendFqdn, "BACKEND_FQDN");
if (hasError) {
process.exit(1);

View file

@ -1,3 +1,32 @@
export const requiredVariables = [
"HOST",
"PORT",
"REDIS_URL",
"REDIS_TTL",
"CASSANDRA_HOST",
"CASSANDRA_PORT",
"CASSANDRA_CONTACT_POINTS",
"CASSANDRA_DATACENTER",
"CASSANDRA_KEYSPACE",
"JWT_SECRET",
"JWT_EXPIRATION",
"JWT_ISSUER",
"FRONTEND_FQDN",
"SMTP_ADDRESS",
"SMTP_PORT",
"SMTP_FROM",
"SMTP_USERNAME",
"SMTP_PASSWORD",
"EXTRA_COMPANY_NAME",
"EXTRA_SUPPORT_EMAIL",
];
export * from "./server";
export * from "./validation";
export * from "./database";

View file

@ -1,6 +1,6 @@
const reqLoggerIgnores = {
ignoredStartsWith: ["/public"],
ignoredPaths: [""],
ignoredPaths: ["/favicon.ico"],
};
export { reqLoggerIgnores };

View file

@ -1,4 +1,5 @@
import type { CassandraConfig } from "#types/config";
import type { simpleConfigValidation } from "#types/lib";
function isValidHost(host: string): boolean {
if (!host || host.trim().length === 0) return false;
@ -50,10 +51,9 @@ function isValidDatacenter(datacenter: string, authEnabled: boolean): boolean {
return datacenter.trim().length > 0;
}
function validateCassandraConfig(config: CassandraConfig): {
isValid: boolean;
errors: string[];
} {
function validateCassandraConfig(
config: CassandraConfig,
): simpleConfigValidation {
const errors: string[] = [];
if (!isValidHost(config.host)) {

6
src/environment/extra.ts Normal file
View file

@ -0,0 +1,6 @@
const extraValues = {
companyName: process.env.EXTRA_COMPANY_NAME || "Default Company",
supportEmail: process.env.EXTRA_SUPPORT_EMAIL || "",
};
export { extraValues };

View file

@ -5,28 +5,95 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{subject}}</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
background-color: #1a1a1a;
color: #e0e0e0;
}
h2 {
color: #4a9eff;
margin-bottom: 20px;
}
a {
color: #4a9eff;
}
.button {
display: inline-block;
padding: 12px 24px;
background-color: #2d7a2d;
color: white;
text-decoration: none;
border-radius: 4px;
margin: 10px 0;
}
.expiry-notice {
background-color: #2a2a2a;
border: 1px solid #444;
padding: 10px;
border-radius: 4px;
margin: 15px 0;
color: #ffa500;
}
.fallback-url {
word-break: break-all;
background-color: #2a2a2a;
border: 1px solid #444;
padding: 8px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
color: #999;
}
.footer {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #444;
font-size: 12px;
color: #999;
}
</style>
</head>
<body>
<h2>Welcome to {{companyName}}!</h2>
<p>Hi {{displayName}},</p>
<p>Thank you for registering with {{companyName}}. Your account has been successfully created.</p>
<h2>Account Details:</h2>
<p>
<strong>User ID:</strong> {{id}}<br>
<strong>Verification Status:</strong> {{isVerified}}
</p>
<p>Thanks for signing up! Please verify your email address to activate your account:</p>
<p>To get started, please verify your email address by clicking the link below:</p>
<p><a href="{{verificationUrl}}">Verify Email Address</a></p>
<p><a href="{{verificationUrl}}" class="button">Verify Email Address</a></p>
<div class="expiry-notice">
<strong>⏰ Important:</strong> {{willExpire}} for security reasons.
</div>
<p><strong>If the button doesn't work:</strong></p>
<p>Copy and paste this link into your browser:</p>
<div class="fallback-url">{{verificationUrl}}</div>
<p>Once verified, you'll have full access to your {{companyName}} account!</p>
<p>Questions? Reply to this email or contact us at {{supportEmail}}</p>
<p>If you didn't create this account, please ignore this email.</p>
<hr>
<p>
Best regards,<br>
The {{companyName}} team
</p>
<p>Best regards,<br>The {{companyName}} team</p>
<div class="footer">
<p>User ID: {{id}}</p>
<p>© {{currentYear}} {{companyName}}. All rights reserved.</p>
<p><small>This is an automated message. Please do not reply directly to this email.</small></p>
</div>
</body>
</html>
</html>