diff --git a/package.json b/package.json index 3bf3fb7a2..3e849fa1f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "coolify", "description": "An open-source & self-hostable Heroku / Netlify alternative.", - "version": "2.0.30", + "version": "2.0.31", "license": "AGPL-3.0", "scripts": { "dev": "docker-compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0", @@ -50,6 +50,7 @@ "svelte": "3.46.4", "svelte-check": "2.4.5", "svelte-preprocess": "4.10.4", + "svelte-select": "^4.4.7", "tailwindcss": "3.0.23", "ts-node": "10.6.0", "tslib": "2.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f9bac811..0382aae5a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,6 +46,7 @@ specifiers: svelte-check: 2.4.5 svelte-kit-cookie-session: 2.1.2 svelte-preprocess: 4.10.4 + svelte-select: ^4.4.7 tailwindcss: 3.0.23 tailwindcss-scrollbar: ^0.1.0 ts-node: 10.6.0 @@ -103,6 +104,7 @@ devDependencies: svelte: 3.46.4 svelte-check: 2.4.5_postcss@8.4.7+svelte@3.46.4 svelte-preprocess: 4.10.4_e836cbb8ceb5bfaa7513362dd6308834 + svelte-select: 4.4.7 tailwindcss: 3.0.23_4b9e11f8e85900587b5e2272c5d4c20c ts-node: 10.6.0_e79e62fe450383fd2d418267dc75e645 tslib: 2.3.1 @@ -5277,6 +5279,13 @@ packages: typescript: 4.6.2 dev: true + /svelte-select/4.4.7: + resolution: + { + integrity: sha512-fIf9Z8rPI6F8naHZ9wjXT0Pv5gLyhdHAFkHFJnCfVVfELE8e82uOoF0xEVQP6Kir+b4Q5yOvNAzZ61WbSU6A0A== + } + dev: true + /svelte/3.46.4: resolution: { diff --git a/prisma/migrations/20220320141424_phpmodules/migration.sql b/prisma/migrations/20220320141424_phpmodules/migration.sql new file mode 100644 index 000000000..6a17ff8a3 --- /dev/null +++ b/prisma/migrations/20220320141424_phpmodules/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "phpModules" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 85b78fdc4..2a9a43c2b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -95,6 +95,7 @@ model Application { gitSourceId String? gitSource GitSource? @relation(fields: [gitSourceId], references: [id]) secrets Secret[] + phpModules String? } model ApplicationSettings { diff --git a/src/lib/buildPacks/gatsby.ts b/src/lib/buildPacks/gatsby.ts index 9f2685ff0..3e9c34a2d 100644 --- a/src/lib/buildPacks/gatsby.ts +++ b/src/lib/buildPacks/gatsby.ts @@ -9,6 +9,7 @@ const createDockerfile = async (data, imageforBuild): Promise => { Dockerfile.push('WORKDIR /usr/share/nginx/html'); Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/src/app/${publishDirectory} ./`); + Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`EXPOSE 80`); Dockerfile.push('CMD ["nginx", "-g", "daemon off;"]'); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); diff --git a/src/lib/buildPacks/php.ts b/src/lib/buildPacks/php.ts index 5d53bd468..4903213c6 100644 --- a/src/lib/buildPacks/php.ts +++ b/src/lib/buildPacks/php.ts @@ -4,12 +4,19 @@ import { promises as fs } from 'fs'; const createDockerfile = async (data, image): Promise => { const { workdir, baseDirectory } = data; const Dockerfile: Array = []; - Dockerfile.push(`FROM ${image}`); Dockerfile.push(`LABEL coolify.image=true`); + if (data.phpModules?.length > 0) { + Dockerfile.push( + `ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/` + ); + Dockerfile.push(`RUN chmod +x /usr/local/bin/install-php-extensions`); + Dockerfile.push(`RUN /usr/local/bin/install-php-extensions ${data.phpModules.join(' ')}`); + } Dockerfile.push('RUN a2enmod rewrite'); Dockerfile.push('WORKDIR /var/www/html'); Dockerfile.push(`COPY .${baseDirectory || ''} /var/www/html`); + Dockerfile.push(`COPY /.htaccess /var/www/html/.htaccess`); Dockerfile.push(`EXPOSE 80`); Dockerfile.push('CMD ["apache2-foreground"]'); Dockerfile.push('RUN chown -R www-data /var/www/html'); diff --git a/src/lib/buildPacks/react.ts b/src/lib/buildPacks/react.ts index 5db9c33b2..29c462d02 100644 --- a/src/lib/buildPacks/react.ts +++ b/src/lib/buildPacks/react.ts @@ -9,6 +9,7 @@ const createDockerfile = async (data, image): Promise => { Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push('WORKDIR /usr/share/nginx/html'); Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/src/app/${publishDirectory} ./`); + Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`EXPOSE 80`); Dockerfile.push('CMD ["nginx", "-g", "daemon off;"]'); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); diff --git a/src/lib/buildPacks/static.ts b/src/lib/buildPacks/static.ts index 795f00d7b..075fad0aa 100644 --- a/src/lib/buildPacks/static.ts +++ b/src/lib/buildPacks/static.ts @@ -39,6 +39,7 @@ const createDockerfile = async (data, image): Promise => { } else { Dockerfile.push(`COPY .${baseDirectory || ''} ./`); } + Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`EXPOSE 80`); Dockerfile.push('CMD ["nginx", "-g", "daemon off;"]'); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); diff --git a/src/lib/buildPacks/svelte.ts b/src/lib/buildPacks/svelte.ts index 3ce957a1b..e6821dafe 100644 --- a/src/lib/buildPacks/svelte.ts +++ b/src/lib/buildPacks/svelte.ts @@ -9,6 +9,7 @@ const createDockerfile = async (data, image): Promise => { Dockerfile.push('WORKDIR /usr/share/nginx/html'); Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/src/app/${publishDirectory} ./`); + Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`EXPOSE 80`); Dockerfile.push('CMD ["nginx", "-g", "daemon off;"]'); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); diff --git a/src/lib/buildPacks/vuejs.ts b/src/lib/buildPacks/vuejs.ts index f88f4ac2a..4fe90038b 100644 --- a/src/lib/buildPacks/vuejs.ts +++ b/src/lib/buildPacks/vuejs.ts @@ -9,6 +9,7 @@ const createDockerfile = async (data, image): Promise => { Dockerfile.push('WORKDIR /usr/share/nginx/html'); Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/src/app/${publishDirectory} ./`); + Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`EXPOSE 80`); Dockerfile.push('CMD ["nginx", "-g", "daemon off;"]'); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); @@ -19,6 +20,21 @@ export default async function (data) { const image = 'nginx:stable-alpine'; const imageForBuild = 'node:lts'; await buildCacheImageWithNode(data, imageForBuild); + // await fs.writeFile(`${data.workdir}/default.conf`, `server { + // listen 80; + // server_name localhost; + + // location / { + // root /usr/share/nginx/html; + // try_files $uri $uri/ /index.html; + // } + + // error_page 500 502 503 504 /50x.html; + // location = /50x.html { + // root /usr/share/nginx/html; + // } + // } + // `); await createDockerfile(data, image); await buildImage(data); } catch (error) { diff --git a/src/lib/database/applications.ts b/src/lib/database/applications.ts index 2dab7b46c..d5285e79a 100644 --- a/src/lib/database/applications.ts +++ b/src/lib/database/applications.ts @@ -156,6 +156,9 @@ export async function getApplication({ id, teamId }) { return s; }); } + if (body?.phpModules) { + body.phpModules = body.phpModules.split(','); + } return { ...body }; } @@ -211,7 +214,8 @@ export async function configureApplication({ buildCommand, startCommand, baseDirectory, - publishDirectory + publishDirectory, + phpModules }) { return await prisma.application.update({ where: { id }, @@ -224,7 +228,8 @@ export async function configureApplication({ startCommand, baseDirectory, publishDirectory, - name + name, + phpModules } }); } diff --git a/src/lib/queues/builder.ts b/src/lib/queues/builder.ts index 0430276e3..f0617d328 100644 --- a/src/lib/queues/builder.ts +++ b/src/lib/queues/builder.ts @@ -45,13 +45,25 @@ export default async function (job) { publishDirectory, projectId, secrets, + phpModules, type, pullmergeRequestId = null, sourceBranch = null, settings } = job.data; const { debug } = settings; + await asyncSleep(1000); + + await db.prisma.build.updateMany({ + where: { + status: 'queued', + id: { not: buildId }, + applicationId, + createdAt: { lt: new Date(new Date().getTime() - 60 * 60 * 1000) } + }, + data: { status: 'failed' } + }); let imageId = applicationId; let domain = getDomain(fqdn); const isHttps = fqdn.startsWith('https://'); @@ -179,7 +191,8 @@ export default async function (job) { buildCommand, startCommand, baseDirectory, - secrets + secrets, + phpModules }); else { saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId }); diff --git a/src/routes/applications/[id]/index.json.ts b/src/routes/applications/[id]/index.json.ts index 3b4405706..47fc9e2ae 100644 --- a/src/routes/applications/[id]/index.json.ts +++ b/src/routes/applications/[id]/index.json.ts @@ -52,7 +52,8 @@ export const post: RequestHandler = async (event) => { buildCommand, startCommand, baseDirectory, - publishDirectory + publishDirectory, + phpModules } = await event.request.json(); if (port) port = Number(port); @@ -68,7 +69,8 @@ export const post: RequestHandler = async (event) => { buildCommand, startCommand, baseDirectory, - publishDirectory + publishDirectory, + phpModules }); return { status: 201 }; } catch (error) { diff --git a/src/routes/applications/[id]/index.svelte b/src/routes/applications/[id]/index.svelte index a0ccec921..221aefddc 100644 --- a/src/routes/applications/[id]/index.svelte +++ b/src/routes/applications/[id]/index.svelte @@ -47,7 +47,125 @@ import { post } from '$lib/api'; import cuid from 'cuid'; import { browser } from '$app/env'; + import Select from 'svelte-select'; const { id } = $page.params; + let collection = [ + 'amqp', + 'apcu', + 'apcu_bc', + 'ast', + 'bcmath', + 'blackfire', + 'bz2', + 'calendar', + 'cmark', + 'csv', + 'dba', + 'decimal', + 'ds', + 'enchant', + 'ev', + 'event', + 'excimer', + 'exif', + 'ffi', + 'gd', + 'gearman', + 'geoip', + 'geospatial', + 'gettext', + 'gmagick', + 'gmp', + 'gnupg', + 'grpc', + 'http', + 'igbinary', + 'imagick', + 'imap', + 'inotify', + 'interbase', + 'intl', + 'ioncube_loader', + 'jsmin', + 'json_post', + 'ldap', + 'lzf', + 'mailparse', + 'maxminddb', + 'mcrypt', + 'memcache', + 'memcached', + 'mongo', + 'mongodb', + 'mosquitto', + 'msgpack', + 'mssql', + 'mysqli', + 'oauth', + 'oci8', + 'odbc', + 'opcache', + 'opencensus', + 'openswoole', + 'parallel', + 'pcntl', + 'pcov', + 'pdo_dblib', + 'pdo_firebird', + 'pdo_mysql', + 'pdo_oci', + 'pdo_odbc', + 'pdo_pgsql', + 'pdo_sqlsrv', + 'pgsql', + 'propro', + 'protobuf', + 'pspell', + 'pthreads', + 'raphf', + 'rdkafka', + 'recode', + 'redis', + 'seaslog', + 'shmop', + 'smbclient', + 'snmp', + 'snuffleupagus', + 'soap', + 'sockets', + 'solr', + 'sourceguardian', + 'spx', + 'sqlsrv', + 'ssh2', + 'stomp', + 'swoole', + 'sybase_ct', + 'sysvmsg', + 'sysvsem', + 'sysvshm', + 'tensor', + 'tidy', + 'timezonedb', + 'uopz', + 'uploadprogress', + 'uuid', + 'vips', + 'wddx', + 'xdebug', + 'xhprof', + 'xlswriter', + 'xmldiff', + 'xmlrpc', + 'xsl', + 'yac', + 'yaml', + 'yar', + 'zephir_parser', + 'zip', + 'zookeeper', + 'zstd' + ]; let domainEl: HTMLInputElement; @@ -57,7 +175,6 @@ let previews = application.settings.previews; let dualCerts = application.settings.dualCerts; let autodeploy = application.settings.autodeploy; - if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) { application.fqdn = `http://${cuid()}.demo.coolify.io`; } @@ -108,8 +225,9 @@ async function handleSubmit() { loading = true; try { + const tempPhpModules = application.phpModules?.map((module) => module.value).toString() || ''; await post(`/applications/${id}/check.json`, { fqdn: application.fqdn, forceSave }); - await post(`/applications/${id}.json`, { ...application }); + await post(`/applications/${id}.json`, { ...application, phpModules: tempPhpModules }); return window.location.reload(); } catch ({ error }) { if (error.startsWith('DNS not set')) { @@ -363,7 +481,19 @@ /> {/if} - + {#if application.buildPack === 'php'} +
+ +
+