Some checks failed
CI / skip-ci-check (push) Successful in 1m27s
CI / skip-ci-check (pull_request) Successful in 1m27s
CI / lint-and-type-check (pull_request) Has been cancelled
CI / python-lint (pull_request) Has been cancelled
CI / test-backend (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
CI / secret-scanning (pull_request) Has been cancelled
CI / dependency-scan (pull_request) Has been cancelled
CI / sast-scan (pull_request) Has been cancelled
CI / workflow-summary (pull_request) Has been cancelled
CI / lint-and-type-check (push) Successful in 2m4s
CI / python-lint (push) Successful in 1m53s
CI / test-backend (push) Successful in 2m37s
CI / build (push) Failing after 2m13s
CI / secret-scanning (push) Successful in 1m40s
CI / dependency-scan (push) Successful in 1m34s
CI / sast-scan (push) Successful in 2m42s
CI / workflow-summary (push) Successful in 1m26s
This commit modifies the `.gitignore` file to exclude Python library directories while ensuring the viewer-frontend's `lib` directory is not ignored. It also updates the `package.json` to activate the virtual environment during backend tests, improving the testing process. Additionally, the CI workflow is enhanced to prevent duplicate runs for branches with open pull requests. Various components in the viewer frontend are updated to ensure consistent naming conventions and improve type safety. These changes contribute to a cleaner codebase and a more efficient development workflow.
87 lines
2.0 KiB
TypeScript
87 lines
2.0 KiB
TypeScript
import { clsx, type ClassValue } from "clsx"
|
|
import { twMerge } from "tailwind-merge"
|
|
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs))
|
|
}
|
|
|
|
/**
|
|
* Validates an email address format
|
|
* @param email - The email address to validate
|
|
* @returns true if the email is valid, false otherwise
|
|
*/
|
|
export function isValidEmail(email: string): boolean {
|
|
if (!email || typeof email !== 'string') {
|
|
return false;
|
|
}
|
|
|
|
// Trim whitespace
|
|
const trimmedEmail = email.trim();
|
|
|
|
// Basic email regex pattern
|
|
// Matches: local-part@domain
|
|
// - Local part: alphanumeric, dots, hyphens, underscores, plus signs
|
|
// - Domain: alphanumeric, dots, hyphens
|
|
// - Must have @ symbol
|
|
// - Domain must have at least one dot
|
|
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
|
|
// Check length constraints (RFC 5321)
|
|
if (trimmedEmail.length > 254) {
|
|
return false;
|
|
}
|
|
|
|
// Check for valid format
|
|
if (!emailRegex.test(trimmedEmail)) {
|
|
return false;
|
|
}
|
|
|
|
// Additional checks
|
|
// - Cannot start or end with dot
|
|
// - Cannot have consecutive dots
|
|
// - Must have valid domain part
|
|
const parts = trimmedEmail.split('@');
|
|
if (parts.length !== 2) {
|
|
return false;
|
|
}
|
|
|
|
const [localPart, domain] = parts;
|
|
|
|
// Validate local part
|
|
if (localPart.length === 0 || localPart.length > 64) {
|
|
return false;
|
|
}
|
|
if (localPart.startsWith('.') || localPart.endsWith('.')) {
|
|
return false;
|
|
}
|
|
if (localPart.includes('..')) {
|
|
return false;
|
|
}
|
|
|
|
// Validate domain part
|
|
if (domain.length === 0 || domain.length > 253) {
|
|
return false;
|
|
}
|
|
if (domain.startsWith('.') || domain.endsWith('.')) {
|
|
return false;
|
|
}
|
|
if (domain.includes('..')) {
|
|
return false;
|
|
}
|
|
if (!domain.includes('.')) {
|
|
return false;
|
|
}
|
|
|
|
// Domain must have at least one TLD (top-level domain)
|
|
const domainParts = domain.split('.');
|
|
if (domainParts.length < 2) {
|
|
return false;
|
|
}
|
|
const tld = domainParts[domainParts.length - 1];
|
|
if (tld.length < 2) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|