harden media
This commit is contained in:
@@ -12,6 +12,7 @@ import { DriverName } from '../value-objects/driver/DriverName';
|
||||
import { CountryCode } from '../value-objects/CountryCode';
|
||||
import { DriverBio } from '../value-objects/driver/DriverBio';
|
||||
import { JoinedAt } from '../value-objects/JoinedAt';
|
||||
import { MediaReference } from '@core/domain/media/MediaReference';
|
||||
|
||||
export class Driver implements IEntity<string> {
|
||||
readonly id: string;
|
||||
@@ -21,6 +22,7 @@ export class Driver implements IEntity<string> {
|
||||
readonly bio: DriverBio | undefined;
|
||||
readonly joinedAt: JoinedAt;
|
||||
readonly category: string | undefined;
|
||||
readonly avatarRef: MediaReference;
|
||||
|
||||
private constructor(props: {
|
||||
id: string;
|
||||
@@ -30,6 +32,7 @@ export class Driver implements IEntity<string> {
|
||||
bio?: DriverBio;
|
||||
joinedAt: JoinedAt;
|
||||
category?: string;
|
||||
avatarRef: MediaReference;
|
||||
}) {
|
||||
this.id = props.id;
|
||||
this.iracingId = props.iracingId;
|
||||
@@ -38,6 +41,7 @@ export class Driver implements IEntity<string> {
|
||||
this.bio = props.bio;
|
||||
this.joinedAt = props.joinedAt;
|
||||
this.category = props.category;
|
||||
this.avatarRef = props.avatarRef;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,6 +55,7 @@ export class Driver implements IEntity<string> {
|
||||
bio?: string;
|
||||
joinedAt?: Date;
|
||||
category?: string;
|
||||
avatarRef?: MediaReference;
|
||||
}): Driver {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Driver ID is required');
|
||||
@@ -64,12 +69,14 @@ export class Driver implements IEntity<string> {
|
||||
bio?: DriverBio;
|
||||
joinedAt: JoinedAt;
|
||||
category?: string;
|
||||
avatarRef: MediaReference;
|
||||
} = {
|
||||
id: props.id,
|
||||
iracingId: IRacingId.create(props.iracingId),
|
||||
name: DriverName.create(props.name),
|
||||
country: CountryCode.create(props.country),
|
||||
joinedAt: JoinedAt.create(props.joinedAt ?? new Date()),
|
||||
avatarRef: props.avatarRef ?? MediaReference.createSystemDefault('avatar'),
|
||||
};
|
||||
|
||||
if (props.bio !== undefined) {
|
||||
@@ -90,6 +97,7 @@ export class Driver implements IEntity<string> {
|
||||
bio?: string;
|
||||
joinedAt: Date;
|
||||
category?: string;
|
||||
avatarRef?: MediaReference;
|
||||
}): Driver {
|
||||
const driverProps: {
|
||||
id: string;
|
||||
@@ -99,12 +107,14 @@ export class Driver implements IEntity<string> {
|
||||
bio?: DriverBio;
|
||||
joinedAt: JoinedAt;
|
||||
category?: string;
|
||||
avatarRef: MediaReference;
|
||||
} = {
|
||||
id: props.id,
|
||||
iracingId: IRacingId.create(props.iracingId),
|
||||
name: DriverName.create(props.name),
|
||||
country: CountryCode.create(props.country),
|
||||
joinedAt: JoinedAt.create(props.joinedAt),
|
||||
avatarRef: props.avatarRef ?? MediaReference.createSystemDefault('avatar'),
|
||||
};
|
||||
|
||||
if (props.bio !== undefined) {
|
||||
@@ -125,11 +135,13 @@ export class Driver implements IEntity<string> {
|
||||
country: string;
|
||||
bio: string | undefined;
|
||||
category: string | undefined;
|
||||
avatarRef: MediaReference;
|
||||
}>): Driver {
|
||||
const nextName = 'name' in props ? DriverName.create(props.name!) : this.name;
|
||||
const nextCountry = 'country' in props ? CountryCode.create(props.country!) : this.country;
|
||||
const nextBio = 'bio' in props ? (props.bio ? DriverBio.create(props.bio) : undefined) : this.bio;
|
||||
const nextCategory = 'category' in props ? props.category : this.category;
|
||||
const nextAvatarRef = 'avatarRef' in props ? props.avatarRef! : this.avatarRef;
|
||||
|
||||
const driverProps: {
|
||||
id: string;
|
||||
@@ -139,12 +151,14 @@ export class Driver implements IEntity<string> {
|
||||
bio?: DriverBio;
|
||||
joinedAt: JoinedAt;
|
||||
category?: string;
|
||||
avatarRef: MediaReference;
|
||||
} = {
|
||||
id: this.id,
|
||||
iracingId: this.iracingId,
|
||||
name: nextName,
|
||||
country: nextCountry,
|
||||
joinedAt: this.joinedAt,
|
||||
avatarRef: nextAvatarRef,
|
||||
};
|
||||
|
||||
if (nextBio !== undefined) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ParticipantCount } from '../value-objects/ParticipantCount';
|
||||
import { MaxParticipants } from '../value-objects/MaxParticipants';
|
||||
import { SessionDuration } from '../value-objects/SessionDuration';
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
import { MediaReference } from '@core/domain/media/MediaReference';
|
||||
|
||||
/**
|
||||
* Stewarding decision mode for protests
|
||||
@@ -99,6 +100,7 @@ export class League implements IEntity<LeagueId> {
|
||||
readonly category?: string | undefined;
|
||||
readonly createdAt: LeagueCreatedAt;
|
||||
readonly socialLinks: LeagueSocialLinks | undefined;
|
||||
readonly logoRef: MediaReference;
|
||||
|
||||
// Domain state for business rule enforcement
|
||||
private readonly _participantCount: ParticipantCount;
|
||||
@@ -115,6 +117,7 @@ export class League implements IEntity<LeagueId> {
|
||||
socialLinks?: LeagueSocialLinks;
|
||||
participantCount: ParticipantCount;
|
||||
visibility: LeagueVisibility;
|
||||
logoRef: MediaReference;
|
||||
}) {
|
||||
this.id = props.id;
|
||||
this.name = props.name;
|
||||
@@ -126,6 +129,7 @@ export class League implements IEntity<LeagueId> {
|
||||
this.socialLinks = props.socialLinks;
|
||||
this._participantCount = props.participantCount;
|
||||
this._visibility = props.visibility;
|
||||
this.logoRef = props.logoRef;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,6 +150,7 @@ export class League implements IEntity<LeagueId> {
|
||||
websiteUrl?: string;
|
||||
};
|
||||
participantCount?: number;
|
||||
logoRef?: MediaReference;
|
||||
}): League {
|
||||
// Validate required fields
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
@@ -254,6 +259,7 @@ export class League implements IEntity<LeagueId> {
|
||||
...(socialLinks !== undefined ? { socialLinks } : {}),
|
||||
participantCount,
|
||||
visibility,
|
||||
logoRef: props.logoRef ?? MediaReference.createSystemDefault('logo'),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -271,6 +277,7 @@ export class League implements IEntity<LeagueId> {
|
||||
youtubeUrl?: string;
|
||||
websiteUrl?: string;
|
||||
};
|
||||
logoRef?: MediaReference;
|
||||
}): League {
|
||||
const id = LeagueId.create(props.id);
|
||||
const name = LeagueName.create(props.name);
|
||||
@@ -297,6 +304,7 @@ export class League implements IEntity<LeagueId> {
|
||||
...(socialLinks !== undefined ? { socialLinks } : {}),
|
||||
participantCount,
|
||||
visibility,
|
||||
logoRef: props.logoRef ?? MediaReference.createSystemDefault('logo'),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -356,11 +364,13 @@ export class League implements IEntity<LeagueId> {
|
||||
youtubeUrl?: string;
|
||||
websiteUrl?: string;
|
||||
};
|
||||
logoRef: MediaReference;
|
||||
}>): League {
|
||||
const name = props.name ? LeagueName.create(props.name) : this.name;
|
||||
const description = props.description ? LeagueDescription.create(props.description) : this.description;
|
||||
const ownerId = props.ownerId ? LeagueOwnerId.create(props.ownerId) : this.ownerId;
|
||||
const socialLinks = props.socialLinks ? LeagueSocialLinks.create(props.socialLinks) : this.socialLinks;
|
||||
const logoRef = 'logoRef' in props ? props.logoRef! : this.logoRef;
|
||||
|
||||
// If settings are being updated, validate them
|
||||
let newSettings = props.settings ?? this.settings;
|
||||
@@ -427,6 +437,7 @@ export class League implements IEntity<LeagueId> {
|
||||
...(socialLinks !== undefined ? { socialLinks } : {}),
|
||||
participantCount: this._participantCount,
|
||||
visibility: this._visibility,
|
||||
logoRef: logoRef,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -461,6 +472,7 @@ export class League implements IEntity<LeagueId> {
|
||||
...(this.socialLinks !== undefined ? { socialLinks: this.socialLinks } : {}),
|
||||
participantCount: newCount,
|
||||
visibility: this._visibility,
|
||||
logoRef: this.logoRef,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -485,6 +497,7 @@ export class League implements IEntity<LeagueId> {
|
||||
...(this.socialLinks !== undefined ? { socialLinks: this.socialLinks } : {}),
|
||||
participantCount: newCount,
|
||||
visibility: this._visibility,
|
||||
logoRef: this.logoRef,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { TeamDescription } from '../value-objects/TeamDescription';
|
||||
import { DriverId } from './DriverId';
|
||||
import { LeagueId } from './LeagueId';
|
||||
import { TeamCreatedAt } from '../value-objects/TeamCreatedAt';
|
||||
import { MediaReference } from '@core/domain/media/MediaReference';
|
||||
|
||||
export class Team implements IEntity<string> {
|
||||
readonly id: string;
|
||||
@@ -25,6 +26,7 @@ export class Team implements IEntity<string> {
|
||||
readonly category: string | undefined;
|
||||
readonly isRecruiting: boolean;
|
||||
readonly createdAt: TeamCreatedAt;
|
||||
readonly logoRef: MediaReference;
|
||||
|
||||
private constructor(props: {
|
||||
id: string;
|
||||
@@ -36,6 +38,7 @@ export class Team implements IEntity<string> {
|
||||
category: string | undefined;
|
||||
isRecruiting: boolean;
|
||||
createdAt: TeamCreatedAt;
|
||||
logoRef: MediaReference;
|
||||
}) {
|
||||
this.id = props.id;
|
||||
this.name = props.name;
|
||||
@@ -46,6 +49,7 @@ export class Team implements IEntity<string> {
|
||||
this.category = props.category;
|
||||
this.isRecruiting = props.isRecruiting;
|
||||
this.createdAt = props.createdAt;
|
||||
this.logoRef = props.logoRef;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,6 +65,7 @@ export class Team implements IEntity<string> {
|
||||
category?: string;
|
||||
isRecruiting?: boolean;
|
||||
createdAt?: Date;
|
||||
logoRef?: MediaReference;
|
||||
}): Team {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Team ID is required');
|
||||
@@ -80,6 +85,7 @@ export class Team implements IEntity<string> {
|
||||
category: props.category,
|
||||
isRecruiting: props.isRecruiting ?? false,
|
||||
createdAt: TeamCreatedAt.create(props.createdAt ?? new Date()),
|
||||
logoRef: props.logoRef ?? MediaReference.createSystemDefault('logo'),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -93,6 +99,7 @@ export class Team implements IEntity<string> {
|
||||
category?: string;
|
||||
isRecruiting: boolean;
|
||||
createdAt: Date;
|
||||
logoRef?: MediaReference;
|
||||
}): Team {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Team ID is required');
|
||||
@@ -112,6 +119,7 @@ export class Team implements IEntity<string> {
|
||||
category: props.category,
|
||||
isRecruiting: props.isRecruiting,
|
||||
createdAt: TeamCreatedAt.create(props.createdAt),
|
||||
logoRef: props.logoRef ?? MediaReference.createSystemDefault('logo'),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -126,6 +134,7 @@ export class Team implements IEntity<string> {
|
||||
leagues: string[];
|
||||
category: string | undefined;
|
||||
isRecruiting: boolean;
|
||||
logoRef: MediaReference;
|
||||
}>): Team {
|
||||
const nextName = 'name' in props ? TeamName.create(props.name!) : this.name;
|
||||
const nextTag = 'tag' in props ? TeamTag.create(props.tag!) : this.tag;
|
||||
@@ -134,6 +143,7 @@ export class Team implements IEntity<string> {
|
||||
const nextLeagues = 'leagues' in props ? props.leagues!.map(leagueId => LeagueId.create(leagueId)) : this.leagues;
|
||||
const nextCategory = 'category' in props ? props.category : this.category;
|
||||
const nextIsRecruiting = 'isRecruiting' in props ? props.isRecruiting! : this.isRecruiting;
|
||||
const nextLogoRef = 'logoRef' in props ? props.logoRef! : this.logoRef;
|
||||
|
||||
return new Team({
|
||||
id: this.id,
|
||||
@@ -145,6 +155,7 @@ export class Team implements IEntity<string> {
|
||||
category: nextCategory,
|
||||
isRecruiting: nextIsRecruiting,
|
||||
createdAt: this.createdAt,
|
||||
logoRef: nextLogoRef,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Application Port: IMediaRepository
|
||||
*
|
||||
* Repository interface for static media assets (logos, images, icons).
|
||||
* Handles frontend assets like team logos, driver avatars, etc.
|
||||
* Repository interface for media assets (logos, avatars).
|
||||
* Handles frontend assets like team logos and driver avatars.
|
||||
*/
|
||||
|
||||
export interface IMediaRepository {
|
||||
@@ -17,22 +17,17 @@ export interface IMediaRepository {
|
||||
getTeamLogo(teamId: string): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* Get track image URL
|
||||
* Get league logo URL
|
||||
*/
|
||||
getTrackImage(trackId: string): Promise<string | null>;
|
||||
getLeagueLogo(leagueId: string): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* Get category icon URL
|
||||
* Get league cover URL
|
||||
*/
|
||||
getCategoryIcon(categoryId: string): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* Get sponsor logo URL
|
||||
*/
|
||||
getSponsorLogo(sponsorId: string): Promise<string | null>;
|
||||
getLeagueCover(leagueId: string): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* Clear all media data (for reseeding)
|
||||
*/
|
||||
clear(): Promise<void>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
export interface TeamStats {
|
||||
logoUrl: string;
|
||||
performanceLevel: 'beginner' | 'intermediate' | 'advanced' | 'pro';
|
||||
specialization: 'endurance' | 'sprint' | 'mixed';
|
||||
region: string;
|
||||
|
||||
Reference in New Issue
Block a user