refactor: front end code to unified design
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
+103
-175
@@ -1,196 +1,124 @@
|
||||
/**
|
||||
* Identity implementation classes for Mail Manager services
|
||||
* Identity models for Chrono Manager services
|
||||
*/
|
||||
|
||||
import type {
|
||||
ServiceIdentity,
|
||||
ServiceIdentityNone,
|
||||
ServiceIdentityBasic,
|
||||
ServiceIdentityToken,
|
||||
ServiceIdentityOAuth,
|
||||
ServiceIdentityCertificate
|
||||
ServiceIdentity,
|
||||
ServiceIdentityBasic,
|
||||
ServiceIdentityCertificate,
|
||||
ServiceIdentityNone,
|
||||
ServiceIdentityOAuth,
|
||||
ServiceIdentityToken
|
||||
} from '@/types/service';
|
||||
import { clonePlain } from './clone-plain';
|
||||
import { MutationProxy } from './mutation-proxy';
|
||||
|
||||
/**
|
||||
* Base Identity class
|
||||
*/
|
||||
export abstract class Identity {
|
||||
abstract toJson(): ServiceIdentity;
|
||||
|
||||
static fromJson(data: ServiceIdentity): Identity {
|
||||
switch (data.type) {
|
||||
case 'NA':
|
||||
return IdentityNone.fromJson(data);
|
||||
case 'BA':
|
||||
return IdentityBasic.fromJson(data);
|
||||
case 'TA':
|
||||
return IdentityToken.fromJson(data);
|
||||
case 'OA':
|
||||
return IdentityOAuth.fromJson(data);
|
||||
case 'CC':
|
||||
return IdentityCertificate.fromJson(data);
|
||||
default:
|
||||
throw new Error(`Unknown identity type: ${(data as any).type}`);
|
||||
export abstract class Identity<T extends ServiceIdentity = ServiceIdentity> {
|
||||
protected _original: T;
|
||||
protected _mutated: Partial<T>;
|
||||
protected _mutationProxy: MutationProxy<T>;
|
||||
protected _data: T;
|
||||
|
||||
protected constructor(initial: T) {
|
||||
this._original = clonePlain(initial);
|
||||
this._mutated = {};
|
||||
this._mutationProxy = new MutationProxy<T>(() => this._original, () => this._mutated);
|
||||
this._data = this._mutationProxy.create();
|
||||
}
|
||||
|
||||
protected load(data: T): this {
|
||||
this._original = clonePlain(data);
|
||||
this._mutated = {};
|
||||
this._data = this._mutationProxy.create();
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON(): ServiceIdentity { return this.toJson(); }
|
||||
toJson(): T;
|
||||
toJson(delta: true): Partial<T>;
|
||||
toJson(delta?: boolean): T | Partial<T> {
|
||||
return delta
|
||||
? clonePlain(this._mutated)
|
||||
: { ...clonePlain(this._original), ...clonePlain(this._mutated) };
|
||||
}
|
||||
|
||||
abstract clone(): Identity;
|
||||
mutated(): boolean { return Reflect.ownKeys(this._mutated).length > 0; }
|
||||
|
||||
static fromJson(data: ServiceIdentity): Identity {
|
||||
switch (data.type) {
|
||||
case 'NA': return IdentityNone.fromJson(data);
|
||||
case 'BA': return IdentityBasic.fromJson(data);
|
||||
case 'TA': return IdentityToken.fromJson(data);
|
||||
case 'OA': return IdentityOAuth.fromJson(data);
|
||||
case 'CC': return IdentityCertificate.fromJson(data);
|
||||
default: throw new Error(`Unknown identity type: ${(data as any).type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No authentication
|
||||
*/
|
||||
export class IdentityNone extends Identity {
|
||||
readonly type = 'NA' as const;
|
||||
|
||||
static fromJson(_data: ServiceIdentityNone): IdentityNone {
|
||||
return new IdentityNone();
|
||||
}
|
||||
|
||||
toJson(): ServiceIdentityNone {
|
||||
return {
|
||||
type: this.type
|
||||
};
|
||||
}
|
||||
export class IdentityNone extends Identity<ServiceIdentityNone> {
|
||||
constructor() { super({ type: 'NA' }); }
|
||||
static fromJson(_data: ServiceIdentityNone): IdentityNone { return new IdentityNone(); }
|
||||
clone(): IdentityNone { return IdentityNone.fromJson(this.toJson()); }
|
||||
get type(): 'NA' { return this._data.type; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic authentication (username/password)
|
||||
*/
|
||||
export class IdentityBasic extends Identity {
|
||||
readonly type = 'BA' as const;
|
||||
identity: string;
|
||||
secret: string;
|
||||
|
||||
constructor(identity: string = '', secret: string = '') {
|
||||
super();
|
||||
this.identity = identity;
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
static fromJson(data: ServiceIdentityBasic): IdentityBasic {
|
||||
return new IdentityBasic(data.identity, data.secret);
|
||||
}
|
||||
|
||||
toJson(): ServiceIdentityBasic {
|
||||
return {
|
||||
type: this.type,
|
||||
identity: this.identity,
|
||||
secret: this.secret
|
||||
};
|
||||
}
|
||||
export class IdentityBasic extends Identity<ServiceIdentityBasic> {
|
||||
constructor(identity: string = '', secret: string = '') { super({ type: 'BA', identity, secret }); }
|
||||
static fromJson(data: ServiceIdentityBasic): IdentityBasic { return new IdentityBasic().load(data); }
|
||||
clone(): IdentityBasic { return IdentityBasic.fromJson(this.toJson()); }
|
||||
get type(): 'BA' { return this._data.type; }
|
||||
get identity(): string { return this._data.identity; }
|
||||
set identity(value: string) { this._data.identity = value; }
|
||||
get secret(): string { return this._data.secret; }
|
||||
set secret(value: string) { this._data.secret = value; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Token authentication (API key, static token)
|
||||
*/
|
||||
export class IdentityToken extends Identity {
|
||||
readonly type = 'TA' as const;
|
||||
token: string;
|
||||
|
||||
constructor(token: string = '') {
|
||||
super();
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
static fromJson(data: ServiceIdentityToken): IdentityToken {
|
||||
return new IdentityToken(data.token);
|
||||
}
|
||||
|
||||
toJson(): ServiceIdentityToken {
|
||||
return {
|
||||
type: this.type,
|
||||
token: this.token
|
||||
};
|
||||
}
|
||||
export class IdentityToken extends Identity<ServiceIdentityToken> {
|
||||
constructor(token: string = '') { super({ type: 'TA', token }); }
|
||||
static fromJson(data: ServiceIdentityToken): IdentityToken { return new IdentityToken().load(data); }
|
||||
clone(): IdentityToken { return IdentityToken.fromJson(this.toJson()); }
|
||||
get type(): 'TA' { return this._data.type; }
|
||||
get token(): string { return this._data.token; }
|
||||
set token(value: string) { this._data.token = value; }
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth authentication
|
||||
*/
|
||||
export class IdentityOAuth extends Identity {
|
||||
readonly type = 'OA' as const;
|
||||
accessToken: string;
|
||||
accessScope?: string[];
|
||||
accessExpiry?: number;
|
||||
refreshToken?: string;
|
||||
refreshLocation?: string;
|
||||
export class IdentityOAuth extends Identity<ServiceIdentityOAuth> {
|
||||
constructor(accessToken: string = '', accessScope?: string[], accessExpiry?: number, refreshToken?: string, refreshLocation?: string) {
|
||||
super({ type: 'OA', accessToken, accessScope, accessExpiry, refreshToken, refreshLocation });
|
||||
}
|
||||
|
||||
constructor(
|
||||
accessToken: string = '',
|
||||
accessScope?: string[],
|
||||
accessExpiry?: number,
|
||||
refreshToken?: string,
|
||||
refreshLocation?: string
|
||||
) {
|
||||
super();
|
||||
this.accessToken = accessToken;
|
||||
this.accessScope = accessScope;
|
||||
this.accessExpiry = accessExpiry;
|
||||
this.refreshToken = refreshToken;
|
||||
this.refreshLocation = refreshLocation;
|
||||
}
|
||||
static fromJson(data: ServiceIdentityOAuth): IdentityOAuth { return new IdentityOAuth().load(data); }
|
||||
clone(): IdentityOAuth { return IdentityOAuth.fromJson(this.toJson()); }
|
||||
isExpired(): boolean { return this.accessExpiry ? Date.now() / 1000 >= this.accessExpiry : false; }
|
||||
expiresIn(): number { return this.accessExpiry ? Math.max(0, this.accessExpiry - Date.now() / 1000) : Infinity; }
|
||||
|
||||
static fromJson(data: ServiceIdentityOAuth): IdentityOAuth {
|
||||
return new IdentityOAuth(
|
||||
data.accessToken,
|
||||
data.accessScope,
|
||||
data.accessExpiry,
|
||||
data.refreshToken,
|
||||
data.refreshLocation
|
||||
);
|
||||
}
|
||||
|
||||
toJson(): ServiceIdentityOAuth {
|
||||
return {
|
||||
type: this.type,
|
||||
accessToken: this.accessToken,
|
||||
...(this.accessScope && { accessScope: this.accessScope }),
|
||||
...(this.accessExpiry && { accessExpiry: this.accessExpiry }),
|
||||
...(this.refreshToken && { refreshToken: this.refreshToken }),
|
||||
...(this.refreshLocation && { refreshLocation: this.refreshLocation })
|
||||
};
|
||||
}
|
||||
|
||||
isExpired(): boolean {
|
||||
if (!this.accessExpiry) return false;
|
||||
return Date.now() / 1000 >= this.accessExpiry;
|
||||
}
|
||||
|
||||
expiresIn(): number {
|
||||
if (!this.accessExpiry) return Infinity;
|
||||
return Math.max(0, this.accessExpiry - Date.now() / 1000);
|
||||
}
|
||||
get type(): 'OA' { return this._data.type; }
|
||||
get accessToken(): string { return this._data.accessToken; }
|
||||
set accessToken(value: string) { this._data.accessToken = value; }
|
||||
get accessScope(): string[] | undefined { return this._data.accessScope ? [...this._data.accessScope] : undefined; }
|
||||
set accessScope(value: string[] | undefined) { this._data.accessScope = value ? [...value] : undefined; }
|
||||
get accessExpiry(): number | undefined { return this._data.accessExpiry; }
|
||||
set accessExpiry(value: number | undefined) { this._data.accessExpiry = value; }
|
||||
get refreshToken(): string | undefined { return this._data.refreshToken; }
|
||||
set refreshToken(value: string | undefined) { this._data.refreshToken = value; }
|
||||
get refreshLocation(): string | undefined { return this._data.refreshLocation; }
|
||||
set refreshLocation(value: string | undefined) { this._data.refreshLocation = value; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Client certificate authentication (mTLS)
|
||||
*/
|
||||
export class IdentityCertificate extends Identity {
|
||||
readonly type = 'CC' as const;
|
||||
certificate: string;
|
||||
privateKey: string;
|
||||
passphrase?: string;
|
||||
export class IdentityCertificate extends Identity<ServiceIdentityCertificate> {
|
||||
constructor(certificate: string = '', privateKey: string = '', passphrase?: string) {
|
||||
super({ type: 'CC', certificate, privateKey, passphrase });
|
||||
}
|
||||
|
||||
constructor(certificate: string = '', privateKey: string = '', passphrase?: string) {
|
||||
super();
|
||||
this.certificate = certificate;
|
||||
this.privateKey = privateKey;
|
||||
this.passphrase = passphrase;
|
||||
}
|
||||
|
||||
static fromJson(data: ServiceIdentityCertificate): IdentityCertificate {
|
||||
return new IdentityCertificate(
|
||||
data.certificate,
|
||||
data.privateKey,
|
||||
data.passphrase
|
||||
);
|
||||
}
|
||||
|
||||
toJson(): ServiceIdentityCertificate {
|
||||
return {
|
||||
type: this.type,
|
||||
certificate: this.certificate,
|
||||
privateKey: this.privateKey,
|
||||
...(this.passphrase && { passphrase: this.passphrase })
|
||||
};
|
||||
}
|
||||
static fromJson(data: ServiceIdentityCertificate): IdentityCertificate { return new IdentityCertificate().load(data); }
|
||||
clone(): IdentityCertificate { return IdentityCertificate.fromJson(this.toJson()); }
|
||||
get type(): 'CC' { return this._data.type; }
|
||||
get certificate(): string { return this._data.certificate; }
|
||||
set certificate(value: string) { this._data.certificate = value; }
|
||||
get privateKey(): string { return this._data.privateKey; }
|
||||
set privateKey(value: string) { this._data.privateKey = value; }
|
||||
get passphrase(): string | undefined { return this._data.passphrase; }
|
||||
set passphrase(value: string | undefined) { this._data.passphrase = value; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user