Initial commit
This commit is contained in:
31
src/utils/key-generator.ts
Normal file
31
src/utils/key-generator.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Utility functions for generating unique identifiers
|
||||
*/
|
||||
|
||||
const globalCrypto = typeof globalThis !== "undefined" ? globalThis.crypto : undefined;
|
||||
|
||||
export const generateUuid = (): string => {
|
||||
if (globalCrypto?.randomUUID) {
|
||||
return globalCrypto.randomUUID();
|
||||
}
|
||||
|
||||
const template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
|
||||
return template.replace(/[xy]/g, char => {
|
||||
const randomNibble = Math.floor(Math.random() * 16);
|
||||
const value = char === "x" ? randomNibble : (randomNibble & 0x3) | 0x8;
|
||||
return value.toString(16);
|
||||
});
|
||||
};
|
||||
|
||||
export const generateKey = (): string => {
|
||||
if (globalCrypto?.randomUUID) {
|
||||
return globalCrypto.randomUUID().replace(/-/g, "");
|
||||
}
|
||||
|
||||
if (globalCrypto?.getRandomValues) {
|
||||
const [value] = globalCrypto.getRandomValues(new Uint32Array(1));
|
||||
return value.toString(16);
|
||||
}
|
||||
|
||||
return Math.random().toString(16).slice(2);
|
||||
};
|
||||
276
src/utils/serviceHelpers.ts
Normal file
276
src/utils/serviceHelpers.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
/**
|
||||
* Helper functions for working with service identity and location types
|
||||
*/
|
||||
|
||||
import type {
|
||||
ServiceIdentity,
|
||||
ServiceIdentityNone,
|
||||
ServiceIdentityBasic,
|
||||
ServiceIdentityToken,
|
||||
ServiceIdentityOAuth,
|
||||
ServiceIdentityCertificate,
|
||||
ServiceLocation,
|
||||
ServiceLocationUri,
|
||||
ServiceLocationSocketSole,
|
||||
ServiceLocationSocketSplit,
|
||||
ServiceLocationFile
|
||||
} from '@/types/service';
|
||||
|
||||
// ==================== Identity Helpers ====================
|
||||
|
||||
/**
|
||||
* Create a "None" identity (no authentication)
|
||||
*/
|
||||
export function createIdentityNone(): ServiceIdentityNone {
|
||||
return { type: 'NA' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Basic Auth identity
|
||||
*/
|
||||
export function createIdentityBasic(identity: string, secret: string): ServiceIdentityBasic {
|
||||
return {
|
||||
type: 'BA',
|
||||
identity,
|
||||
secret
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Token Auth identity
|
||||
*/
|
||||
export function createIdentityToken(token: string): ServiceIdentityToken {
|
||||
return {
|
||||
type: 'TA',
|
||||
token
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an OAuth identity
|
||||
*/
|
||||
export function createIdentityOAuth(
|
||||
accessToken: string,
|
||||
options?: {
|
||||
accessScope?: string[];
|
||||
accessExpiry?: number;
|
||||
refreshToken?: string;
|
||||
refreshLocation?: string;
|
||||
}
|
||||
): ServiceIdentityOAuth {
|
||||
return {
|
||||
type: 'OA',
|
||||
accessToken,
|
||||
...options
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Certificate identity
|
||||
*/
|
||||
export function createIdentityCertificate(
|
||||
certificate: string,
|
||||
privateKey: string,
|
||||
passphrase?: string
|
||||
): ServiceIdentityCertificate {
|
||||
return {
|
||||
type: 'CC',
|
||||
certificate,
|
||||
privateKey,
|
||||
...(passphrase && { passphrase })
|
||||
};
|
||||
}
|
||||
|
||||
// ==================== Location Helpers ====================
|
||||
|
||||
/**
|
||||
* Create a URI-based location
|
||||
*/
|
||||
export function createLocationUri(
|
||||
host: string,
|
||||
options?: {
|
||||
scheme?: 'http' | 'https';
|
||||
port?: number;
|
||||
path?: string;
|
||||
verifyPeer?: boolean;
|
||||
verifyHost?: boolean;
|
||||
}
|
||||
): ServiceLocationUri {
|
||||
return {
|
||||
type: 'URI',
|
||||
scheme: options?.scheme || 'https',
|
||||
host,
|
||||
port: options?.port || (options?.scheme === 'http' ? 80 : 443),
|
||||
...(options?.path && { path: options.path }),
|
||||
verifyPeer: options?.verifyPeer ?? true,
|
||||
verifyHost: options?.verifyHost ?? true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a URI location from a full URL string
|
||||
*/
|
||||
export function createLocationFromUrl(url: string): ServiceLocationUri {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
return {
|
||||
type: 'URI',
|
||||
scheme: parsed.protocol.replace(':', '') as 'http' | 'https',
|
||||
host: parsed.hostname,
|
||||
port: parsed.port ? parseInt(parsed.port) : (parsed.protocol === 'https:' ? 443 : 80),
|
||||
path: parsed.pathname,
|
||||
verifyPeer: true,
|
||||
verifyHost: true
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid URL: ${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single socket location (IMAP, SMTP on same server)
|
||||
*/
|
||||
export function createLocationSocketSole(
|
||||
host: string,
|
||||
port: number,
|
||||
encryption: 'none' | 'ssl' | 'tls' | 'starttls' = 'ssl',
|
||||
options?: {
|
||||
verifyPeer?: boolean;
|
||||
verifyHost?: boolean;
|
||||
}
|
||||
): ServiceLocationSocketSole {
|
||||
return {
|
||||
type: 'SOCKET_SOLE',
|
||||
host,
|
||||
port,
|
||||
encryption,
|
||||
verifyPeer: options?.verifyPeer ?? true,
|
||||
verifyHost: options?.verifyHost ?? true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a split socket location (separate IMAP/SMTP servers)
|
||||
*/
|
||||
export function createLocationSocketSplit(
|
||||
config: {
|
||||
inboundHost: string;
|
||||
inboundPort: number;
|
||||
inboundEncryption?: 'none' | 'ssl' | 'tls' | 'starttls';
|
||||
outboundHost: string;
|
||||
outboundPort: number;
|
||||
outboundEncryption?: 'none' | 'ssl' | 'tls' | 'starttls';
|
||||
inboundVerifyPeer?: boolean;
|
||||
inboundVerifyHost?: boolean;
|
||||
outboundVerifyPeer?: boolean;
|
||||
outboundVerifyHost?: boolean;
|
||||
}
|
||||
): ServiceLocationSocketSplit {
|
||||
return {
|
||||
type: 'SOCKET_SPLIT',
|
||||
inboundHost: config.inboundHost,
|
||||
inboundPort: config.inboundPort,
|
||||
inboundEncryption: config.inboundEncryption || 'ssl',
|
||||
outboundHost: config.outboundHost,
|
||||
outboundPort: config.outboundPort,
|
||||
outboundEncryption: config.outboundEncryption || 'ssl',
|
||||
inboundVerifyPeer: config.inboundVerifyPeer ?? true,
|
||||
inboundVerifyHost: config.inboundVerifyHost ?? true,
|
||||
outboundVerifyPeer: config.outboundVerifyPeer ?? true,
|
||||
outboundVerifyHost: config.outboundVerifyHost ?? true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file-based location
|
||||
*/
|
||||
export function createLocationFile(path: string): ServiceLocationFile {
|
||||
return {
|
||||
type: 'FILE',
|
||||
path
|
||||
};
|
||||
}
|
||||
|
||||
// ==================== Validation Helpers ====================
|
||||
|
||||
/**
|
||||
* Validate that an identity object is properly formed
|
||||
*/
|
||||
export function validateIdentity(identity: ServiceIdentity): boolean {
|
||||
switch (identity.type) {
|
||||
case 'NA':
|
||||
return true;
|
||||
case 'BA':
|
||||
return !!(identity as ServiceIdentityBasic).identity &&
|
||||
!!(identity as ServiceIdentityBasic).secret;
|
||||
case 'TA':
|
||||
return !!(identity as ServiceIdentityToken).token;
|
||||
case 'OA':
|
||||
return !!(identity as ServiceIdentityOAuth).accessToken;
|
||||
case 'CC':
|
||||
return !!(identity as ServiceIdentityCertificate).certificate &&
|
||||
!!(identity as ServiceIdentityCertificate).privateKey;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a location object is properly formed
|
||||
*/
|
||||
export function validateLocation(location: ServiceLocation): boolean {
|
||||
switch (location.type) {
|
||||
case 'URI':
|
||||
return !!(location as ServiceLocationUri).host &&
|
||||
!!(location as ServiceLocationUri).port;
|
||||
case 'SOCKET_SOLE':
|
||||
return !!(location as ServiceLocationSocketSole).host &&
|
||||
!!(location as ServiceLocationSocketSole).port;
|
||||
case 'SOCKET_SPLIT':
|
||||
const split = location as ServiceLocationSocketSplit;
|
||||
return !!split.inboundHost && !!split.inboundPort &&
|
||||
!!split.outboundHost && !!split.outboundPort;
|
||||
case 'FILE':
|
||||
return !!(location as ServiceLocationFile).path;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Type Guards ====================
|
||||
|
||||
export function isIdentityNone(identity: ServiceIdentity): identity is ServiceIdentityNone {
|
||||
return identity.type === 'NA';
|
||||
}
|
||||
|
||||
export function isIdentityBasic(identity: ServiceIdentity): identity is ServiceIdentityBasic {
|
||||
return identity.type === 'BA';
|
||||
}
|
||||
|
||||
export function isIdentityToken(identity: ServiceIdentity): identity is ServiceIdentityToken {
|
||||
return identity.type === 'TA';
|
||||
}
|
||||
|
||||
export function isIdentityOAuth(identity: ServiceIdentity): identity is ServiceIdentityOAuth {
|
||||
return identity.type === 'OA';
|
||||
}
|
||||
|
||||
export function isIdentityCertificate(identity: ServiceIdentity): identity is ServiceIdentityCertificate {
|
||||
return identity.type === 'CC';
|
||||
}
|
||||
|
||||
export function isLocationUri(location: ServiceLocation): location is ServiceLocationUri {
|
||||
return location.type === 'URI';
|
||||
}
|
||||
|
||||
export function isLocationSocketSole(location: ServiceLocation): location is ServiceLocationSocketSole {
|
||||
return location.type === 'SOCKET_SOLE';
|
||||
}
|
||||
|
||||
export function isLocationSocketSplit(location: ServiceLocation): location is ServiceLocationSocketSplit {
|
||||
return location.type === 'SOCKET_SPLIT';
|
||||
}
|
||||
|
||||
export function isLocationFile(location: ServiceLocation): location is ServiceLocationFile {
|
||||
return location.type === 'FILE';
|
||||
}
|
||||
Reference in New Issue
Block a user