landvex: Fixar och tester klara för alla komponenter

- Datafabrik: Dockerfile fix, agentorkestrering fungerar
- Vision: Identify-modell, FAISS, OCR alla testade
- API: Alla 7 integrationstester passerade
- Upplösare: Entitetsupplösning verifierad
This commit is contained in:
Bernt
2026-07-05 06:41:32 +00:00
parent f4f853d94b
commit aee0f09db8
19583 changed files with 1450867 additions and 1153 deletions
+98
View File
@@ -0,0 +1,98 @@
/**
* API Key Domain Model
*
* Represents an API key for external integrations.
* Immutable value object pattern with factory methods.
*/
export type ApiKeyId = string;
export type ApiKeyStatus = 'active' | 'revoked' | 'expired';
export interface ApiKeyProps {
id: ApiKeyId;
name: string;
keyHash: string; // SHA-256 hash of the actual key (only stored once)
keyPrefix: string; // First 12 chars of key for display (e.g., "lvx_Af7x9K2m")
status: ApiKeyStatus;
rateLimitPerMinute: number;
rateLimitPerHour: number;
rateLimitPerDay: number;
createdAt: Date;
expiresAt: Date | null;
revokedAt: Date | null;
rotatedFromId: ApiKeyId | null;
metadata: Record<string, unknown>;
}
export class ApiKey {
readonly id: ApiKeyId;
readonly name: string;
readonly keyHash: string;
readonly keyPrefix: string;
readonly status: ApiKeyStatus;
readonly rateLimitPerMinute: number;
readonly rateLimitPerHour: number;
readonly rateLimitPerDay: number;
readonly createdAt: Date;
readonly expiresAt: Date | null;
readonly revokedAt: Date | null;
readonly rotatedFromId: ApiKeyId | null;
readonly metadata: Record<string, unknown>;
constructor(props: ApiKeyProps) {
this.id = props.id;
this.name = props.name;
this.keyHash = props.keyHash;
this.keyPrefix = props.keyPrefix;
this.status = props.status;
this.rateLimitPerMinute = props.rateLimitPerMinute;
this.rateLimitPerHour = props.rateLimitPerHour;
this.rateLimitPerDay = props.rateLimitPerDay;
this.createdAt = props.createdAt;
this.expiresAt = props.expiresAt;
this.revokedAt = props.revokedAt;
this.rotatedFromId = props.rotatedFromId;
this.metadata = props.metadata;
}
isActive(): boolean {
if (this.status !== 'active') return false;
if (this.expiresAt && new Date() > this.expiresAt) return false;
return true;
}
isRevoked(): boolean {
return this.status === 'revoked';
}
isExpired(): boolean {
if (this.status === 'expired') return true;
if (this.expiresAt && new Date() > this.expiresAt) return true;
return false;
}
revoke(): ApiKey {
return new ApiKey({
...this,
status: 'revoked',
revokedAt: new Date(),
});
}
toJSON() {
return {
id: this.id,
name: this.name,
keyPrefix: this.keyPrefix,
status: this.status,
rateLimitPerMinute: this.rateLimitPerMinute,
rateLimitPerHour: this.rateLimitPerHour,
rateLimitPerDay: this.rateLimitPerDay,
createdAt: this.createdAt.toISOString(),
expiresAt: this.expiresAt?.toISOString() ?? null,
revokedAt: this.revokedAt?.toISOString() ?? null,
rotatedFromId: this.rotatedFromId,
metadata: this.metadata,
};
}
}
+7
View File
@@ -106,6 +106,13 @@ export {
enable,
disable,
} from './auth/feature-flags';
export {
ApiKey,
ApiKeyId,
ApiKeyStatus,
ApiKeyProps,
} from './auth/api-key';
export * from './common/errors';
// Artifacts