9e3347cb71
Backend: - Add dbUsername/dbPassword columns to Application entity - Add optional DB credential fields to CreateApplicationDto - Auto-generate dbPassword (crypto.randomBytes) and default dbUsername='appuser' when databaseType != 'none' on app creation - Store both username and password in K8s DB secret (was password-only) - Read DB_USER/POSTGRES_USER/MYSQL_USER from secretKeyRef instead of hardcoded - New restoreDatabaseDump() in KubernetesService: creates K8s Job with psql/mysql client to restore uploaded SQL dump, waits for completion, returns logs - New POST /applications/:id/db-upload endpoint with 500MB file limit Frontend: - Add dbUsername/dbPassword to Application and CreateApplicationDto types - Deploy page: show username/password fields when database is selected, with generate-random-password button and show/hide toggle - App detail page: new Database section with connection info (host, port, db name, username, password with copy-to-clipboard), SQL dump upload area with drag-and-drop, and restore output logs display Security: - Database remains ClusterIP only (no external exposure) - Credentials stored in K8s Secrets (base64-encoded) - Dump file uploaded as temporary K8s Secret, auto-cleaned after restore
102 lines
2.2 KiB
TypeScript
102 lines
2.2 KiB
TypeScript
import {
|
|
Entity,
|
|
PrimaryGeneratedColumn,
|
|
Column,
|
|
CreateDateColumn,
|
|
UpdateDateColumn,
|
|
ManyToOne,
|
|
OneToMany,
|
|
JoinColumn,
|
|
} from 'typeorm';
|
|
import { AppRuntime, DatabaseType } from '../../common/enums';
|
|
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({ nullable: true })
|
|
description: string;
|
|
|
|
@Column({ type: 'enum', enum: AppRuntime })
|
|
runtime: AppRuntime;
|
|
|
|
@Column({ type: 'enum', enum: DatabaseType, default: DatabaseType.NONE })
|
|
databaseType: DatabaseType;
|
|
|
|
@Column({ nullable: true })
|
|
dbUsername: string;
|
|
|
|
@Column({ nullable: true })
|
|
dbPassword: string;
|
|
|
|
@Column({ nullable: true })
|
|
gitUrl: string;
|
|
|
|
@Column({ nullable: true })
|
|
gitToken: string; // Personal access token for private repos
|
|
|
|
@Column({ nullable: true })
|
|
gitBranch: string; // Branch to clone (default: main)
|
|
|
|
@Column({ nullable: true })
|
|
codePath: string; // Path to uploaded zip
|
|
|
|
@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({ nullable: true })
|
|
clusterId: string;
|
|
|
|
@Column({ 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
|
|
|
|
@CreateDateColumn()
|
|
createdAt: Date;
|
|
|
|
@UpdateDateColumn()
|
|
updatedAt: Date;
|
|
}
|