Compare commits

...

8 Commits

Author SHA1 Message Date
Andras Bacsai
3ef093c7e6 Merge pull request #286 from coollabsio/v2.3.2
v2.3.2
2022-04-04 19:40:39 +02:00
Andras Bacsai
f5dfaa81d3 chore:version++ 2022-04-04 19:35:17 +02:00
Andras Bacsai
fcf206a081 fix: Add default webhook domain for n8n 2022-04-04 19:18:06 +02:00
Andras Bacsai
9790d2b613 fix(php): If .htaccess file found use apache 2022-04-04 18:47:22 +02:00
Andras Bacsai
201fa82efc Merge pull request #284 from coollabsio/v2.3.1
v2.3.1
2022-04-04 14:00:28 +02:00
Andras Bacsai
d28433ee64 fix: Default configuration 2022-04-04 13:55:11 +02:00
Andras Bacsai
cc348bf0f5 chore: Version++ 2022-04-04 13:49:37 +02:00
Andras Bacsai
b023d65fcf fix: Secrets build/runtime coudl be changed after save 2022-04-04 13:49:26 +02:00
8 changed files with 93 additions and 43 deletions

View File

@@ -1,7 +1,7 @@
{ {
"name": "coolify", "name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.", "description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "2.3.0", "version": "2.3.2",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev", "dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev",

View File

@@ -102,12 +102,9 @@ export const setDefaultConfiguration = async (data) => {
else if (buildPack === 'php') port = 80; else if (buildPack === 'php') port = 80;
else if (buildPack === 'python') port = 8000; else if (buildPack === 'python') port = 8000;
} }
if (template) { if (!installCommand) installCommand = template?.installCommand || 'yarn install';
if (!installCommand) installCommand = template?.installCommand || 'yarn install'; if (!startCommand) startCommand = template?.startCommand || 'yarn start';
if (!startCommand) startCommand = template?.startCommand || 'yarn start'; if (!buildCommand) buildCommand = template?.buildCommand || null;
if (!buildCommand) buildCommand = template?.buildCommand || null;
}
if (!publishDirectory) publishDirectory = template?.publishDirectory || null; if (!publishDirectory) publishDirectory = template?.publishDirectory || null;
if (baseDirectory) { if (baseDirectory) {
if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`; if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`;
@@ -127,18 +124,7 @@ export const setDefaultConfiguration = async (data) => {
export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId) { export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId) {
try { try {
// TODO: Write full .dockerignore for all deployments!!
if (buildPack === 'php') { if (buildPack === 'php') {
await fs.writeFile(
`${workdir}/.htaccess`,
`
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ index.php [QSA,L]
`
);
await fs.writeFile(`${workdir}/entrypoint.sh`, `chown -R 1000 /app`); await fs.writeFile(`${workdir}/entrypoint.sh`, `chown -R 1000 /app`);
await saveBuildLog({ await saveBuildLog({
line: 'Copied default configuration file for PHP.', line: 'Copied default configuration file for PHP.',

View File

@@ -1,23 +1,33 @@
import { buildImage } from '$lib/docker'; import { buildImage } from '$lib/docker';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
const createDockerfile = async (data, image): Promise<void> => { const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
const { workdir, baseDirectory } = data; const { workdir, baseDirectory } = data;
const Dockerfile: Array<string> = []; const Dockerfile: Array<string> = [];
Dockerfile.push(`FROM ${image}`); Dockerfile.push(`FROM ${image}`);
Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push(`LABEL coolify.image=true`);
Dockerfile.push('WORKDIR /app'); Dockerfile.push('WORKDIR /app');
Dockerfile.push(`COPY .${baseDirectory || ''} /app`); Dockerfile.push(`COPY .${baseDirectory || ''} /app`);
Dockerfile.push(`COPY /.htaccess .`); if (htaccessFound) {
Dockerfile.push(`COPY .${baseDirectory || ''}/.htaccess ./`);
}
Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`); Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`);
Dockerfile.push(`EXPOSE 80`); Dockerfile.push(`EXPOSE 80`);
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
}; };
export default async function (data) { export default async function (data) {
const { workdir, baseDirectory } = data;
try { try {
const image = 'webdevops/php-nginx'; let htaccessFound = false;
await createDockerfile(data, image); try {
const d = await fs.readFile(`${workdir}${baseDirectory || ''}/.htaccess`);
htaccessFound = true;
} catch (e) {
//
}
const image = htaccessFound ? 'webdevops/php-apache' : 'webdevops/php-nginx';
await createDockerfile(data, image, htaccessFound);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@@ -30,7 +30,7 @@
await Promise.all( await Promise.all(
batchSecretsPairs.map(({ name, value, isNew }) => batchSecretsPairs.map(({ name, value, isNew }) =>
limit(() => saveSecret({ name, value, applicationId: id, isNew, dispatch })) limit(() => saveSecret({ name, value, applicationId: id, isNew }))
) )
); );
batchSecrets = ''; batchSecrets = '';
@@ -39,8 +39,8 @@
</script> </script>
<h2 class="title my-6 font-bold">Paste .env file</h2> <h2 class="title my-6 font-bold">Paste .env file</h2>
<form on:submit={getValues} class="mb-12 w-full"> <form on:submit|preventDefault={getValues} class="mb-12 w-full">
<textarea value={batchSecrets} on:change={setBatchValue} class="mb-2 min-h-[200px] w-full" /> <textarea bind:value={batchSecrets} class="mb-2 min-h-[200px] w-full" />
<button <button
class="bg-green-600 hover:bg-green-500 disabled:text-white disabled:opacity-40" class="bg-green-600 hover:bg-green-500 disabled:text-white disabled:opacity-40"
type="submit">Batch add secrets</button type="submit">Batch add secrets</button

View File

@@ -12,6 +12,7 @@
import { del } from '$lib/api'; import { del } from '$lib/api';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import { errorNotification } from '$lib/form'; import { errorNotification } from '$lib/form';
import { toast } from '@zerodevx/svelte-toast';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { saveSecret } from './utils'; import { saveSecret } from './utils';
@@ -32,21 +33,39 @@
} }
async function createSecret(isNew) { async function createSecret(isNew) {
saveSecret({ await saveSecret({
isNew, isNew,
name, name,
value, value,
isBuildSecret, isBuildSecret,
isPRMRSecret, isPRMRSecret,
isNewSecret, isNewSecret,
applicationId: id, applicationId: id
dispatch
}); });
if (isNewSecret) {
name = '';
value = '';
isBuildSecret = false;
}
dispatch('refresh');
toast.push('Secret saved');
} }
function setSecretValue() { async function setSecretValue() {
if (isNewSecret) { if (!isPRMRSecret) {
isBuildSecret = !isBuildSecret; isBuildSecret = !isBuildSecret;
if (!isNewSecret) {
await saveSecret({
isNew: isNewSecret,
name,
value,
isBuildSecret,
isPRMRSecret,
isNewSecret,
applicationId: id
});
toast.push('Secret saved');
}
} }
} }
</script> </script>
@@ -81,9 +100,9 @@
class="relative inline-flex h-6 w-11 flex-shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out" class="relative inline-flex h-6 w-11 flex-shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out"
class:bg-green-600={isBuildSecret} class:bg-green-600={isBuildSecret}
class:bg-stone-700={!isBuildSecret} class:bg-stone-700={!isBuildSecret}
class:opacity-50={!isNewSecret} class:opacity-50={isPRMRSecret}
class:cursor-not-allowed={!isNewSecret} class:cursor-not-allowed={isPRMRSecret}
class:cursor-pointer={isNewSecret} class:cursor-pointer={!isPRMRSecret}
> >
<span class="sr-only">Use isBuildSecret</span> <span class="sr-only">Use isBuildSecret</span>
<span <span

View File

@@ -22,18 +22,48 @@
<script lang="ts"> <script lang="ts">
export let secrets; export let secrets;
export let application; export let application;
import pLimit from 'p-limit';
import Secret from './_Secret.svelte'; import Secret from './_Secret.svelte';
import { getDomain } from '$lib/components/common';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { get } from '$lib/api'; import { get } from '$lib/api';
import BatchSecrets from './_BatchSecrets.svelte'; import { saveSecret } from './utils';
import { toast } from '@zerodevx/svelte-toast';
const limit = pLimit(1);
const { id } = $page.params; const { id } = $page.params;
let batchSecrets = '';
async function refreshSecrets() { async function refreshSecrets() {
const data = await get(`/applications/${id}/secrets.json`); const data = await get(`/applications/${id}/secrets.json`);
secrets = [...data.secrets]; secrets = [...data.secrets];
} }
async function getValues(e) {
e.preventDefault();
const eachValuePair = batchSecrets.split('\n');
const batchSecretsPairs = eachValuePair
.filter((secret) => !secret.startsWith('#') && secret)
.map((secret) => {
const [name, value] = secret.split('=');
const cleanValue = value?.replaceAll('"', '') || '';
return {
name,
value: cleanValue,
isNew: !secrets.find((secret) => name === secret.name)
};
});
await Promise.all(
batchSecretsPairs.map(({ name, value, isNew }) =>
limit(() => saveSecret({ name, value, applicationId: id, isNew }))
)
);
batchSecrets = '';
await refreshSecrets();
toast.push('Secrets saved');
}
function asd() {
console.log(secrets);
}
</script> </script>
<div class="flex items-center space-x-2 p-5 px-6 font-bold"> <div class="flex items-center space-x-2 p-5 px-6 font-bold">
@@ -134,6 +164,13 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<button on:click={asd}>Save</button>
<BatchSecrets {secrets} {id} {refreshSecrets} /> <h2 class="title my-6 font-bold">Paste .env file</h2>
<form on:submit|preventDefault={getValues} class="mb-12 w-full">
<textarea bind:value={batchSecrets} class="mb-2 min-h-[200px] w-full" />
<button
class="bg-green-600 hover:bg-green-500 disabled:text-white disabled:opacity-40"
type="submit">Batch add secrets</button
>
</form>
</div> </div>

View File

@@ -10,7 +10,6 @@ type Props = {
isPRMRSecret?: boolean; isPRMRSecret?: boolean;
isNewSecret?: boolean; isNewSecret?: boolean;
applicationId: string; applicationId: string;
dispatch: (name: string) => void;
}; };
export async function saveSecret({ export async function saveSecret({
@@ -20,8 +19,7 @@ export async function saveSecret({
isBuildSecret, isBuildSecret,
isPRMRSecret, isPRMRSecret,
isNewSecret, isNewSecret,
applicationId, applicationId
dispatch
}: Props): Promise<void> { }: Props): Promise<void> {
if (!name) return errorNotification('Name is required.'); if (!name) return errorNotification('Name is required.');
if (!value) return errorNotification('Value is required.'); if (!value) return errorNotification('Value is required.');
@@ -33,13 +31,11 @@ export async function saveSecret({
isPRMRSecret, isPRMRSecret,
isNew: isNew || false isNew: isNew || false
}); });
dispatch('refresh');
if (isNewSecret) { if (isNewSecret) {
name = ''; name = '';
value = ''; value = '';
isBuildSecret = false; isBuildSecret = false;
} }
toast.push('Secret saved.');
} catch ({ error }) { } catch ({ error }) {
return errorNotification(error); return errorNotification(error);
} }

View File

@@ -24,7 +24,9 @@ export const post: RequestHandler = async (event) => {
const config = { const config = {
image: `${image}:${version}`, image: `${image}:${version}`,
volume: `${id}-n8n:/root/.n8n`, volume: `${id}-n8n:/root/.n8n`,
environmentVariables: {} environmentVariables: {
WEBHOOK_URL: `${service.fqdn}`
}
}; };
if (serviceSecret.length > 0) { if (serviceSecret.length > 0) {
serviceSecret.forEach((secret) => { serviceSecret.forEach((secret) => {