feat: add WordPress as runtime — Dockerfile, K8s env vars, wp-content PVC, frontend UI
Backend: - Add WORDPRESS to AppRuntime enum - Add wordpressDockerfile() using wordpress:6-php8.3-apache base image with custom theme/plugin/wp-content merge support - Add WordPress-specific K8s env vars (WORDPRESS_DB_HOST, WORDPRESS_DB_USER, WORDPRESS_DB_PASSWORD, WORDPRESS_DB_NAME, WORDPRESS_TABLE_PREFIX) - Create wp-content PersistentVolumeClaim (2Gi) for WordPress deployments - Mount wp-content PVC in deployment container at /var/www/html/wp-content Frontend: - Add 'wordpress' to runtime type unions (Application, CreateApplicationDto) - Add WordPress runtime card in deploy page (port 80, blue icon) - Auto-select MySQL database when WordPress is chosen, disable other DB options - Show Persian hint 'وردپرس به MySQL نیاز دارد' when WordPress selected - Update all runtime icon colors across dashboard, apps, admin/apps pages to show blue-600 for WordPress
This commit is contained in:
@@ -288,6 +288,8 @@ export class BuildService {
|
|||||||
return this.nodeDockerfile(app);
|
return this.nodeDockerfile(app);
|
||||||
case AppRuntime.LARAVEL:
|
case AppRuntime.LARAVEL:
|
||||||
return this.laravelDockerfile(app);
|
return this.laravelDockerfile(app);
|
||||||
|
case AppRuntime.WORDPRESS:
|
||||||
|
return this.wordpressDockerfile(app);
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported runtime: ${app.runtime}`);
|
throw new Error(`Unsupported runtime: ${app.runtime}`);
|
||||||
}
|
}
|
||||||
@@ -384,6 +386,39 @@ CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private wordpressDockerfile(app: Application): string {
|
||||||
|
return `FROM wordpress:6-php8.3-apache
|
||||||
|
|
||||||
|
# Install additional PHP extensions commonly needed by WordPress
|
||||||
|
RUN docker-php-ext-install opcache
|
||||||
|
|
||||||
|
# Enable Apache mod_rewrite for pretty permalinks
|
||||||
|
RUN a2enmod rewrite
|
||||||
|
|
||||||
|
# Copy user's custom themes, plugins, and uploads if provided
|
||||||
|
COPY . /tmp/user-content
|
||||||
|
|
||||||
|
# Merge user content into the WordPress installation
|
||||||
|
# - wp-content/themes, wp-content/plugins, wp-content/uploads
|
||||||
|
# - Also support full WordPress roots (with wp-config.php, etc.)
|
||||||
|
RUN if [ -d /tmp/user-content/wp-content ]; then \\
|
||||||
|
cp -a /tmp/user-content/wp-content/. /var/www/html/wp-content/; \\
|
||||||
|
fi && \\
|
||||||
|
if [ -f /tmp/user-content/wp-config.php ]; then \\
|
||||||
|
cp /tmp/user-content/wp-config.php /var/www/html/wp-config.php; \\
|
||||||
|
fi && \\
|
||||||
|
# Copy any loose PHP files (custom root files)
|
||||||
|
find /tmp/user-content -maxdepth 1 -name "*.php" ! -name "wp-config.php" -exec cp {} /var/www/html/ \\\\; 2>/dev/null || true && \\
|
||||||
|
rm -rf /tmp/user-content
|
||||||
|
|
||||||
|
# Set proper ownership
|
||||||
|
RUN chown -R www-data:www-data /var/www/html
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["apache2-foreground"]
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
private async waitForJobCompletion(
|
private async waitForJobCompletion(
|
||||||
batchApi: k8s.BatchV1Api,
|
batchApi: k8s.BatchV1Api,
|
||||||
coreApi: k8s.CoreV1Api,
|
coreApi: k8s.CoreV1Api,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export enum TicketPriority {
|
|||||||
export enum AppRuntime {
|
export enum AppRuntime {
|
||||||
NODEJS = 'nodejs',
|
NODEJS = 'nodejs',
|
||||||
LARAVEL = 'laravel',
|
LARAVEL = 'laravel',
|
||||||
|
WORDPRESS = 'wordpress',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum DatabaseType {
|
export enum DatabaseType {
|
||||||
|
|||||||
@@ -115,6 +115,11 @@ export class KubernetesService implements OnModuleInit {
|
|||||||
manifests.database = await this.deployDatabase(coreApi, appsApi, context);
|
manifests.database = await this.deployDatabase(coreApi, appsApi, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3.5 Create wp-content PVC for WordPress
|
||||||
|
if (context.runtime === AppRuntime.WORDPRESS) {
|
||||||
|
manifests.wpContentPvc = await this.applyWordPressPvc(coreApi, context);
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Create Deployment
|
// 4. Create Deployment
|
||||||
manifests.deployment = await this.applyDeployment(appsApi, context);
|
manifests.deployment = await this.applyDeployment(appsApi, context);
|
||||||
|
|
||||||
@@ -194,6 +199,17 @@ export class KubernetesService implements OnModuleInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WordPress-specific env vars (official image expects these)
|
||||||
|
if (ctx.runtime === AppRuntime.WORDPRESS && ctx.databaseType === DatabaseType.MYSQL) {
|
||||||
|
extraEnv.push(
|
||||||
|
{ name: 'WORDPRESS_DB_HOST', value: `${ctx.appName}-db:3306` },
|
||||||
|
{ name: 'WORDPRESS_DB_NAME', value: ctx.appName.replace(/-/g, '_') },
|
||||||
|
{ name: 'WORDPRESS_DB_USER', valueFrom: { secretKeyRef: { name: `${ctx.appName}-db-secret`, key: 'username' } } },
|
||||||
|
{ name: 'WORDPRESS_DB_PASSWORD', valueFrom: { secretKeyRef: { name: `${ctx.appName}-db-secret`, key: 'password' } } },
|
||||||
|
{ name: 'WORDPRESS_TABLE_PREFIX', value: 'wp_' },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const deployment: k8s.V1Deployment = {
|
const deployment: k8s.V1Deployment = {
|
||||||
apiVersion: 'apps/v1',
|
apiVersion: 'apps/v1',
|
||||||
kind: 'Deployment',
|
kind: 'Deployment',
|
||||||
@@ -230,8 +246,30 @@ export class KubernetesService implements OnModuleInit {
|
|||||||
periodSeconds: 10,
|
periodSeconds: 10,
|
||||||
failureThreshold: 5,
|
failureThreshold: 5,
|
||||||
},
|
},
|
||||||
|
...(ctx.runtime === AppRuntime.WORDPRESS
|
||||||
|
? {
|
||||||
|
volumeMounts: [
|
||||||
|
{
|
||||||
|
name: 'wp-content',
|
||||||
|
mountPath: '/var/www/html/wp-content',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
...(ctx.runtime === AppRuntime.WORDPRESS
|
||||||
|
? {
|
||||||
|
volumes: [
|
||||||
|
{
|
||||||
|
name: 'wp-content',
|
||||||
|
persistentVolumeClaim: {
|
||||||
|
claimName: `${ctx.appName}-wp-content`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -245,6 +283,34 @@ export class KubernetesService implements OnModuleInit {
|
|||||||
return deployment;
|
return deployment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async applyWordPressPvc(coreApi: k8s.CoreV1Api, ctx: ManifestContext): Promise<any> {
|
||||||
|
const pvcName = `${ctx.appName}-wp-content`;
|
||||||
|
const pvc = {
|
||||||
|
apiVersion: 'v1',
|
||||||
|
kind: 'PersistentVolumeClaim',
|
||||||
|
metadata: {
|
||||||
|
name: pvcName,
|
||||||
|
namespace: ctx.namespace,
|
||||||
|
labels: { app: ctx.appName },
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
accessModes: ['ReadWriteOnce'],
|
||||||
|
resources: {
|
||||||
|
requests: { storage: '2Gi' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await coreApi.readNamespacedPersistentVolumeClaim(pvcName, ctx.namespace);
|
||||||
|
this.logger.log(`PVC ${pvcName} already exists, skipping creation`);
|
||||||
|
} catch {
|
||||||
|
await coreApi.createNamespacedPersistentVolumeClaim(ctx.namespace, pvc);
|
||||||
|
this.logger.log(`Created WordPress PVC: ${pvcName}`);
|
||||||
|
}
|
||||||
|
return pvc;
|
||||||
|
}
|
||||||
|
|
||||||
private async applyService(coreApi: k8s.CoreV1Api, ctx: ManifestContext): Promise<any> {
|
private async applyService(coreApi: k8s.CoreV1Api, ctx: ManifestContext): Promise<any> {
|
||||||
const service: k8s.V1Service = {
|
const service: k8s.V1Service = {
|
||||||
apiVersion: 'v1',
|
apiVersion: 'v1',
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ export default function AdminAppsPage() {
|
|||||||
<td className="px-6 py-4">
|
<td className="px-6 py-4">
|
||||||
<Link href={`/dashboard/apps/${app.id}`} className="flex items-center gap-3 group">
|
<Link href={`/dashboard/apps/${app.id}`} className="flex items-center gap-3 group">
|
||||||
<div className="w-9 h-9 rounded-lg bg-primary-50 flex items-center justify-center shrink-0">
|
<div className="w-9 h-9 rounded-lg bg-primary-50 flex items-center justify-center shrink-0">
|
||||||
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : 'text-orange-500'}`} />
|
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : app.runtime === 'wordpress' ? 'text-blue-600' : 'text-orange-500'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="font-semibold text-gray-900 group-hover:text-primary-600 transition-colors block">
|
<span className="font-semibold text-gray-900 group-hover:text-primary-600 transition-colors block">
|
||||||
@@ -254,7 +254,7 @@ export default function AdminAppsPage() {
|
|||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-10 h-10 rounded-xl bg-primary-50 flex items-center justify-center">
|
<div className="w-10 h-10 rounded-xl bg-primary-50 flex items-center justify-center">
|
||||||
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : 'text-orange-500'}`} />
|
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : app.runtime === 'wordpress' ? 'text-blue-600' : 'text-orange-500'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-gray-900">{app.name}</h3>
|
<h3 className="font-semibold text-gray-900">{app.name}</h3>
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ export default function AppDetailPage() {
|
|||||||
<div className="flex flex-col sm:flex-row sm:items-center gap-4">
|
<div className="flex flex-col sm:flex-row sm:items-center gap-4">
|
||||||
<div className="flex items-center gap-4 flex-1 min-w-0">
|
<div className="flex items-center gap-4 flex-1 min-w-0">
|
||||||
<div className="w-14 h-14 rounded-2xl bg-primary-50 flex items-center justify-center shrink-0">
|
<div className="w-14 h-14 rounded-2xl bg-primary-50 flex items-center justify-center shrink-0">
|
||||||
<Hexagon className={`w-7 h-7 ${app.runtime === 'nodejs' ? 'text-green-500' : 'text-orange-500'}`} />
|
<Hexagon className={`w-7 h-7 ${app.runtime === 'nodejs' ? 'text-green-500' : app.runtime === 'wordpress' ? 'text-blue-600' : 'text-orange-500'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export default function AppsPage() {
|
|||||||
<td className="px-6 py-4">
|
<td className="px-6 py-4">
|
||||||
<Link href={`/dashboard/apps/${app.id}`} className="flex items-center gap-3 group">
|
<Link href={`/dashboard/apps/${app.id}`} className="flex items-center gap-3 group">
|
||||||
<div className="w-9 h-9 rounded-lg bg-primary-50 flex items-center justify-center shrink-0">
|
<div className="w-9 h-9 rounded-lg bg-primary-50 flex items-center justify-center shrink-0">
|
||||||
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : 'text-orange-500'}`} />
|
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : app.runtime === 'wordpress' ? 'text-blue-600' : 'text-orange-500'}`} />
|
||||||
</div>
|
</div>
|
||||||
<span className="font-semibold text-gray-900 group-hover:text-primary-600 transition-colors">
|
<span className="font-semibold text-gray-900 group-hover:text-primary-600 transition-colors">
|
||||||
{app.name}
|
{app.name}
|
||||||
@@ -149,7 +149,7 @@ export default function AppsPage() {
|
|||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-10 h-10 rounded-xl bg-primary-50 flex items-center justify-center">
|
<div className="w-10 h-10 rounded-xl bg-primary-50 flex items-center justify-center">
|
||||||
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : 'text-orange-500'}`} />
|
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : app.runtime === 'wordpress' ? 'text-blue-600' : 'text-orange-500'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-gray-900">{app.name}</h3>
|
<h3 className="font-semibold text-gray-900">{app.name}</h3>
|
||||||
|
|||||||
@@ -363,15 +363,22 @@ export default function DeployPage() {
|
|||||||
<h2 className="text-lg font-semibold text-gray-900">Runtime & Database</h2>
|
<h2 className="text-lg font-semibold text-gray-900">Runtime & Database</h2>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-3">Application Runtime</label>
|
<label className="block text-sm font-medium text-gray-700 mb-3">Application Runtime</label>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||||
{[
|
{[
|
||||||
{ value: 'nodejs', label: 'Node.js', icon: <Hexagon className="w-6 h-6 text-green-500" />, desc: 'Express, NestJS, Fastify...' },
|
{ value: 'nodejs', label: 'Node.js', icon: <Hexagon className="w-6 h-6 text-green-500" />, desc: 'Express, NestJS, Fastify...' },
|
||||||
{ value: 'laravel', label: 'Laravel', icon: <Hexagon className="w-6 h-6 text-orange-500" />, desc: 'PHP 8.3, Composer, Artisan' },
|
{ value: 'laravel', label: 'Laravel', icon: <Hexagon className="w-6 h-6 text-orange-500" />, desc: 'PHP 8.3, Composer, Artisan' },
|
||||||
|
{ value: 'wordpress', label: 'WordPress', icon: <Hexagon className="w-6 h-6 text-blue-600" />, desc: 'PHP 8.3, Apache, wp-content' },
|
||||||
].map((opt) => (
|
].map((opt) => (
|
||||||
<button
|
<button
|
||||||
key={opt.value}
|
key={opt.value}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setForm({ ...form, runtime: opt.value as any, port: opt.value === 'nodejs' ? 3000 : 8000 })}
|
onClick={() => {
|
||||||
|
const updates: any = { runtime: opt.value as any };
|
||||||
|
if (opt.value === 'nodejs') updates.port = 3000;
|
||||||
|
else if (opt.value === 'laravel') updates.port = 8000;
|
||||||
|
else if (opt.value === 'wordpress') { updates.port = 80; updates.databaseType = 'mysql'; }
|
||||||
|
setForm({ ...form, ...updates });
|
||||||
|
}}
|
||||||
className={`p-4 rounded-xl border-2 text-left transition-colors ${
|
className={`p-4 rounded-xl border-2 text-left transition-colors ${
|
||||||
form.runtime === opt.value ? 'border-primary-500 bg-primary-50' : 'border-gray-200 hover:border-gray-300'
|
form.runtime === opt.value ? 'border-primary-500 bg-primary-50' : 'border-gray-200 hover:border-gray-300'
|
||||||
}`}
|
}`}
|
||||||
@@ -384,25 +391,35 @@ export default function DeployPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-3">Database</label>
|
<label className="block text-sm font-medium text-gray-700 mb-3">
|
||||||
|
Database
|
||||||
|
{form.runtime === 'wordpress' && (
|
||||||
|
<span className="text-xs text-blue-500 mr-2"> — وردپرس به MySQL نیاز دارد</span>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
<div className="grid grid-cols-3 gap-3 sm:gap-4">
|
<div className="grid grid-cols-3 gap-3 sm:gap-4">
|
||||||
{[
|
{[
|
||||||
{ value: 'none', label: 'None', icon: <XCircle className="w-6 h-6 text-gray-400" /> },
|
{ value: 'none', label: 'None', icon: <XCircle className="w-6 h-6 text-gray-400" /> },
|
||||||
{ value: 'postgresql', label: 'PostgreSQL', icon: <svg className="w-6 h-6 text-blue-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5V19C3 20.66 7.03 22 12 22C16.97 22 21 20.66 21 19V5"/><path d="M3 12C3 13.66 7.03 15 12 15C16.97 15 21 13.66 21 12"/></svg> },
|
{ value: 'postgresql', label: 'PostgreSQL', icon: <svg className="w-6 h-6 text-blue-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5V19C3 20.66 7.03 22 12 22C16.97 22 21 20.66 21 19V5"/><path d="M3 12C3 13.66 7.03 15 12 15C16.97 15 21 13.66 21 12"/></svg> },
|
||||||
{ value: 'mysql', label: 'MySQL', icon: <svg className="w-6 h-6 text-orange-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5V19C3 20.66 7.03 22 12 22C16.97 22 21 20.66 21 19V5"/><path d="M3 12C3 13.66 7.03 15 12 15C16.97 15 21 13.66 21 12"/></svg> },
|
{ value: 'mysql', label: 'MySQL', icon: <svg className="w-6 h-6 text-orange-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5V19C3 20.66 7.03 22 12 22C16.97 22 21 20.66 21 19V5"/><path d="M3 12C3 13.66 7.03 15 12 15C16.97 15 21 13.66 21 12"/></svg> },
|
||||||
].map((opt) => (
|
].map((opt) => {
|
||||||
|
const isWordPress = form.runtime === 'wordpress';
|
||||||
|
const disabled = isWordPress && opt.value !== 'mysql';
|
||||||
|
return (
|
||||||
<button
|
<button
|
||||||
key={opt.value}
|
key={opt.value}
|
||||||
type="button"
|
type="button"
|
||||||
|
disabled={disabled}
|
||||||
onClick={() => setForm({ ...form, databaseType: opt.value as any })}
|
onClick={() => setForm({ ...form, databaseType: opt.value as any })}
|
||||||
className={`p-4 rounded-xl border-2 text-center transition-colors ${
|
className={`p-4 rounded-xl border-2 text-center transition-colors ${
|
||||||
form.databaseType === opt.value ? 'border-primary-500 bg-primary-50' : 'border-gray-200 hover:border-gray-300'
|
form.databaseType === opt.value ? 'border-primary-500 bg-primary-50' : 'border-gray-200 hover:border-gray-300'
|
||||||
}`}
|
} ${disabled ? 'opacity-40 cursor-not-allowed' : ''}`}
|
||||||
>
|
>
|
||||||
<div className="flex justify-center">{opt.icon}</div>
|
<div className="flex justify-center">{opt.icon}</div>
|
||||||
<p className="mt-2 font-semibold text-sm text-gray-900">{opt.label}</p>
|
<p className="mt-2 font-semibold text-sm text-gray-900">{opt.label}</p>
|
||||||
</button>
|
</button>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ export default function DashboardPage() {
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="w-11 h-11 rounded-xl bg-primary-50 flex items-center justify-center shrink-0">
|
<div className="w-11 h-11 rounded-xl bg-primary-50 flex items-center justify-center shrink-0">
|
||||||
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : 'text-orange-500'}`} />
|
<Hexagon className={`w-5 h-5 ${app.runtime === 'nodejs' ? 'text-green-600' : app.runtime === 'wordpress' ? 'text-blue-600' : 'text-orange-500'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<h3 className="font-semibold text-gray-900 group-hover:text-primary-700 transition-colors truncate">
|
<h3 className="font-semibold text-gray-900 group-hover:text-primary-700 transition-colors truncate">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export interface Application {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
runtime: 'nodejs' | 'laravel';
|
runtime: 'nodejs' | 'laravel' | 'wordpress';
|
||||||
databaseType: 'mysql' | 'postgresql' | 'none';
|
databaseType: 'mysql' | 'postgresql' | 'none';
|
||||||
dbUsername?: string;
|
dbUsername?: string;
|
||||||
dbPassword?: string;
|
dbPassword?: string;
|
||||||
@@ -87,7 +87,7 @@ export interface AuthResponse {
|
|||||||
export interface CreateApplicationDto {
|
export interface CreateApplicationDto {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
runtime: 'nodejs' | 'laravel';
|
runtime: 'nodejs' | 'laravel' | 'wordpress';
|
||||||
databaseType: 'mysql' | 'postgresql' | 'none';
|
databaseType: 'mysql' | 'postgresql' | 'none';
|
||||||
dbUsername?: string;
|
dbUsername?: string;
|
||||||
dbPassword?: string;
|
dbPassword?: string;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user