Files
cloud-host/backend/src/applications/entities/application.entity.ts
T
keyhan 22359be40e fix(platform): apply production hardening from audit plan
Close billing, tenancy, migration, build, and CI/CD gaps identified in the
audit: wallet/gateway guards, full-UUID namespaces, idempotent migrations with
base schema, stateful service stability, safer Dockerfiles/git builds, and
platform chart hardening (Redis auth, RollingUpdate, backups, Swagger off).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 19:35:07 +03:30

231 lines
6.4 KiB
TypeScript

import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
OneToMany,
JoinColumn,
} from 'typeorm';
import {
AppRuntime,
DatabaseType,
BillingCycle,
AppLifecycleStatus,
CustomDomainStatus,
ProductType,
} from '../../common/enums';
import { Exclude, Expose } from 'class-transformer';
import { User } from '../../users/entities/user.entity';
import { Deployment } from '../../deployments/entities/deployment.entity';
@Entity('applications')
export class Application {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column({ type: 'varchar', default: ProductType.APPLICATION })
productType: ProductType;
@Column({ nullable: true })
description: string;
@Column({ type: 'enum', enum: AppRuntime })
runtime: AppRuntime;
@Column({ type: 'enum', enum: DatabaseType, default: DatabaseType.NONE })
databaseType: DatabaseType;
@Column({ nullable: true })
runtimeVersion: string; // node: '20','18','16' | laravel/wordpress php: '8.3','8.2' | go: '1.22','1.21' | python/django: '3.12','3.11' | dotnet: '8.0','7.0'
@Column({ nullable: true })
phpVersion: string; // PHP version for Laravel/WordPress/PHP (e.g. '8.3', '8.2', '8.1')
@Column({ nullable: true })
dbVersion: string; // e.g. postgres: '17', '16', '15' | mysql: '9.0', '8.4', '8.0'
@Column({ nullable: true })
dbUsername: string;
@Column({ nullable: true })
dbPassword: string;
@Column({ nullable: true, default: '1Gi' })
dbStorageSize: string; // PVC storage size for database (e.g. '1Gi', '5Gi', '10Gi')
@Column({ nullable: true, default: '2Gi' })
appStorageSize: string; // PVC storage size for app files (wp-content, uploads)
// ── Optional Services ─────────────────────────────
@Column({ default: false })
enableRedis: boolean;
@Column({ nullable: true, default: '7.2' })
redisVersion: string;
@Column({ default: false })
enableRabbitmq: boolean;
@Column({ nullable: true, default: '3.13' })
rabbitmqVersion: string;
@Column({ default: false })
enableElasticsearch: boolean; // For application logging
@Column({ nullable: true, default: '8.12' })
elasticsearchVersion: string;
@Column({ type: 'jsonb', nullable: true })
logPaths: string[]; // Custom log paths to collect (e.g., ['/app/logs/*.log'])
/** User-selected CPU/RAM/storage per optional service (database, redis, rabbitmq). */
@Column({ type: 'jsonb', nullable: true })
optionalServiceResources?: {
database?: {
cpuRequest?: string;
cpuLimit: string;
memoryRequest?: string;
memoryLimit: string;
storageGi: number;
};
redis?: {
cpuRequest?: string;
cpuLimit: string;
memoryRequest?: string;
memoryLimit: string;
storageGi: number;
};
rabbitmq?: {
cpuRequest?: string;
cpuLimit: string;
memoryRequest?: string;
memoryLimit: string;
storageGi: number;
};
};
@Column({ nullable: true })
gitUrl: string;
/**
* Personal access token for private repos. Never serialized into API
* responses (see hasGitToken) — it is a credential to an external system.
*/
@Exclude({ toPlainOnly: true })
@Column({ nullable: true })
gitToken: string;
/** Whether a git token is configured (safe indicator for the UI). */
@Expose()
get hasGitToken(): boolean {
return !!this.gitToken;
}
@Column({ nullable: true })
gitBranch: string; // Branch to clone (default: main)
@Column({ nullable: true })
codePath: string; // Path to uploaded zip
@Column({ nullable: true })
dbDumpPath: string; // Path to uploaded SQL dump file
@Column({ type: 'jsonb', nullable: true })
envVars: Record<string, string>;
// Resource configuration
@Column({ default: '100m' })
cpuRequest: string;
@Column({ default: '500m' })
cpuLimit: string;
@Column({ default: '128Mi' })
memoryRequest: string;
@Column({ default: '512Mi' })
memoryLimit: string;
@Column({ default: 1 })
replicas: number;
@Column({ default: 3000 })
port: number;
// Relations
@ManyToOne(() => User, (user: User) => user.applications, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'userId' })
user: User;
@Column()
userId: string;
@Column({ type: 'uuid', nullable: true })
clusterId: string;
@Column({ type: 'uuid', nullable: true })
poolId: string; // Cluster pool used for load-balanced assignment
@OneToMany(() => Deployment, (deployment: Deployment) => deployment.application)
deployments: Deployment[];
// Metadata
@Column({ nullable: true })
latestImageTag: string;
@Column({ nullable: true })
subdomain: string; // <subdomain>.apps.cloudhost.local
// ── Custom Domain ─────────────────────────────
@Column({ nullable: true })
customDomain?: string; // e.g. "www.example.com"
@Column({ type: 'enum', enum: CustomDomainStatus, default: CustomDomainStatus.NONE })
customDomainStatus: CustomDomainStatus;
@Column({ type: 'timestamptz', nullable: true })
customDomainVerifiedAt?: Date;
// ── Billing & Lifecycle ─────────────────────────────
@Column({ nullable: true })
planId: string; // FK to ServicePlan
@Column({ type: 'enum', enum: BillingCycle, nullable: true })
billingCycle: BillingCycle; // The cycle this app was purchased under
@Column({ type: 'enum', enum: AppLifecycleStatus, default: AppLifecycleStatus.ACTIVE })
lifecycleStatus: AppLifecycleStatus;
@Column({ type: 'timestamptz', nullable: true })
planExpiresAt: Date; // When the current billing period ends
@Column({ type: 'timestamptz', nullable: true })
suspendedAt: Date; // When the app was suspended (pods scaled to 0)
/** Per-deployment replica counts captured before suspend (deployment name → replicas). */
@Column({ type: 'jsonb', nullable: true })
suspendedReplicas?: Record<string, number>;
@Column({ type: 'timestamptz', nullable: true })
scheduledDeletionAt: Date; // When the app will be permanently deleted
/** When the user voluntarily removed the app (data docked for restore). */
@Column({ type: 'timestamptz', nullable: true })
dockedAt?: Date;
/** Snapshot captured at dock time — used to restore DB / wp-content / source. */
@Column({ nullable: true })
dockSnapshotId?: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}