Compare commits

...

18 Commits

Author SHA1 Message Date
Andras Bacsai
e39541c318 fix: Traefik middleware 2022-06-09 15:18:21 +02:00
Andras Bacsai
cf88885c94 Merge pull request #452 from coollabsio/next
Fixes for v2.9.7
2022-06-09 14:01:20 +02:00
Andras Bacsai
1192346ce3 fix: remove comments 2022-06-09 14:00:41 +02:00
Andras Bacsai
0e3bd85847 fix: remove console log 2022-06-09 14:00:08 +02:00
Andras Bacsai
edeb6c6965 fix: Plausible script and middlewares 2022-06-09 13:59:09 +02:00
Andras Bacsai
138fd5cb6d Merge pull request #451 from coollabsio/next
v2.9.7
2022-06-09 13:34:02 +02:00
Andras Bacsai
155410bd44 Merge branch 'next' of github.com:coollabsio/coolify into next 2022-06-09 13:31:10 +02:00
Andras Bacsai
20bd829c2e Merge pull request #448 from titouanmathis/feat/gitlab-filter-projects-branches
feat: Add support for search for GitLab applications
2022-06-09 13:31:06 +02:00
Andras Bacsai
7b7e222946 chore: version++ 2022-06-09 13:30:29 +02:00
Andras Bacsai
98d901d06c fix: Plausible custom script 2022-06-08 13:45:42 +02:00
Titouan Mathis
4e862cda6f Add support for accessing all projects and branches for GitLab applications
Fix #236
2022-06-03 12:21:42 +02:00
Andras Bacsai
4e940807ae Merge pull request #446 from coollabsio/next
v2.9.6
2022-06-02 22:05:31 +02:00
Andras Bacsai
b081743f54 fix: pnpm command 2022-06-02 21:59:51 +02:00
Andras Bacsai
34bb9f301f fix: Fider changed an env variable name 2022-06-02 20:37:45 +02:00
Andras Bacsai
ed8a6daeea chore: version++ 2022-06-02 20:37:31 +02:00
Andras Bacsai
9e81ab43ac Merge pull request #445 from coollabsio/next
v2.9.5
2022-06-02 11:24:50 +02:00
Andras Bacsai
32d94cbe97 fix: proxy stop missing argument 2022-06-02 11:13:27 +02:00
Andras Bacsai
46a83aa457 chore: version++ 2022-06-02 11:13:14 +02:00
12 changed files with 168 additions and 100 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.9.4", "version": "2.9.7",
"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 --host 0.0.0.0", "dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0",

View File

@@ -10,8 +10,7 @@ const createDockerfile = async (data, image): Promise<void> => {
Dockerfile.push('WORKDIR /app'); Dockerfile.push('WORKDIR /app');
Dockerfile.push(`LABEL coolify.buildId=${buildId}`); Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
if (isPnpm) { if (isPnpm) {
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm'); Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
Dockerfile.push('RUN pnpm add -g pnpm');
} }
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${baseDirectory || ''} ./`); Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${baseDirectory || ''} ./`);

View File

@@ -35,8 +35,7 @@ const createDockerfile = async (data, image): Promise<void> => {
}); });
} }
if (isPnpm) { if (isPnpm) {
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm'); Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
Dockerfile.push('RUN pnpm add -g pnpm');
} }
Dockerfile.push(`COPY .${baseDirectory || ''} ./`); Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
Dockerfile.push(`RUN ${installCommand}`); Dockerfile.push(`RUN ${installCommand}`);

View File

@@ -36,8 +36,7 @@ const createDockerfile = async (data, image): Promise<void> => {
}); });
} }
if (isPnpm) { if (isPnpm) {
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm'); Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
Dockerfile.push('RUN pnpm add -g pnpm');
} }
Dockerfile.push(`COPY .${baseDirectory || ''} ./`); Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
Dockerfile.push(`RUN ${installCommand}`); Dockerfile.push(`RUN ${installCommand}`);

View File

@@ -35,8 +35,7 @@ const createDockerfile = async (data, image): Promise<void> => {
}); });
} }
if (isPnpm) { if (isPnpm) {
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm'); Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
Dockerfile.push('RUN pnpm add -g pnpm');
} }
Dockerfile.push(`COPY .${baseDirectory || ''} ./`); Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
Dockerfile.push(`RUN ${installCommand}`); Dockerfile.push(`RUN ${installCommand}`);

View File

@@ -66,8 +66,7 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
}); });
} }
if (isPnpm) { if (isPnpm) {
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm'); Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
Dockerfile.push('RUN pnpm add -g pnpm');
} }
if (installCommand) { if (installCommand) {
Dockerfile.push(`COPY .${baseDirectory || ''}/package.json ./`); Dockerfile.push(`COPY .${baseDirectory || ''}/package.json ./`);

View File

@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
export let application; export let application;
export let appId; export let appId;
import Select from 'svelte-select';
import { page, session } from '$app/stores'; import { page, session } from '$app/stores';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { errorNotification } from '$lib/form'; import { errorNotification } from '$lib/form';
@@ -33,6 +34,10 @@
let showSave = false; let showSave = false;
let autodeploy = application.settings.autodeploy || true; let autodeploy = application.settings.autodeploy || true;
let search = {
project: '',
branch: ''
};
let selected = { let selected = {
group: undefined, group: undefined,
project: undefined, project: undefined,
@@ -84,16 +89,49 @@
}, 100); }, 100);
} }
function selectGroup(event) {
selected.group = event.detail;
selected.project = null;
selected.branch = null;
showSave = false;
loadProjects();
}
async function searchProjects(searchText) {
if (!selected.group) {
return;
}
search.project = searchText;
await loadProjects();
return projects;
}
function selectProject(event) {
selected.project = event.detail;
selected.branch = null;
showSave = false;
loadBranches();
}
async function loadProjects() { async function loadProjects() {
const params = new URLSearchParams({
page: 1,
per_page: 25,
archived: false
});
if (search.project) {
params.append('search', search.project);
}
loading.projects = true; loading.projects = true;
if (username === selected.group.name) { if (username === selected.group.name) {
try { try {
projects = await get( params.append('min_access_level', 40);
`${apiUrl}/v4/users/${selected.group.name}/projects?min_access_level=40&page=1&per_page=25&archived=false`, projects = await get(`${apiUrl}/v4/users/${selected.group.name}/projects?${params}`, {
{ Authorization: `Bearer ${$gitTokens.gitlabToken}`
Authorization: `Bearer ${$gitTokens.gitlabToken}` });
}
);
} catch (error) { } catch (error) {
errorNotification(error); errorNotification(error);
throw new Error(error); throw new Error(error);
@@ -102,12 +140,9 @@
} }
} else { } else {
try { try {
projects = await get( projects = await get(`${apiUrl}/v4/groups/${selected.group.id}/projects?${params}`, {
`${apiUrl}/v4/groups/${selected.group.id}/projects?page=1&per_page=25&archived=false`, Authorization: `Bearer ${$gitTokens.gitlabToken}`
{ });
Authorization: `Bearer ${$gitTokens.gitlabToken}`
}
);
} catch (error) { } catch (error) {
errorNotification(error); errorNotification(error);
throw new Error(error); throw new Error(error);
@@ -117,11 +152,35 @@
} }
} }
async function searchBranches(searchText) {
if (!selected.project) {
return;
}
search.branch = searchText;
await loadBranches();
return branches;
}
function selectBranch(event) {
selected.branch = event.detail;
isBranchAlreadyUsed();
}
async function loadBranches() { async function loadBranches() {
const params = new URLSearchParams({
page: 1,
per_page: 100
});
if (search.branch) {
params.append('search', search.branch);
}
loading.branches = true; loading.branches = true;
try { try {
branches = await get( branches = await get(
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?per_page=100&page=1`, `${apiUrl}/v4/projects/${selected.project.id}/repository/branches?${params}`,
{ {
Authorization: `Bearer ${$gitTokens.gitlabToken}` Authorization: `Bearer ${$gitTokens.gitlabToken}`
} }
@@ -267,70 +326,79 @@
<form on:submit={handleSubmit}> <form on:submit={handleSubmit}>
<div class="flex flex-col space-y-2 px-4 xl:flex-row xl:space-y-0 xl:space-x-2 "> <div class="flex flex-col space-y-2 px-4 xl:flex-row xl:space-y-0 xl:space-x-2 ">
{#if loading.base} <div class="custom-select-wrapper">
<select name="group" disabled class="w-96"> <Select
<option selected value="">{$t('application.configuration.loading_groups')}</option> placeholder={loading.base
</select> ? $t('application.configuration.loading_groups')
{:else} : $t('application.configuration.select_a_group')}
<select name="group" class="w-96" bind:value={selected.group} on:change={loadProjects}> id="group"
<option value="" disabled selected>{$t('application.configuration.select_a_group')}</option> showIndicator={!loading.base}
{#each groups as group} isWaiting={loading.base}
<option value={group}>{group.full_name}</option> on:select={selectGroup}
{/each} on:clear={() => {
</select> showSave = false;
{/if} projects = [];
{#if loading.projects} branches = [];
<select name="project" disabled class="w-96"> selected.group = null;
<option selected value="">{$t('application.configuration.loading_projects')}</option> selected.project = null;
</select> selected.branch = null;
{:else if !loading.projects && projects.length > 0} }}
<select value={selected.group}
name="project" isDisabled={loading.base}
class="w-96" isClearable={false}
bind:value={selected.project} items={groups}
on:change={loadBranches} labelIdentifier="full_name"
disabled={!selected.group} optionIdentifier="id"
> />
<option value="" disabled selected </div>
>{$t('application.configuration.select_a_project')}</option <div class="custom-select-wrapper">
> <Select
{#each projects as project} placeholder={loading.projects
<option value={project}>{project.name}</option> ? $t('application.configuration.loading_projects')
{/each} : $t('application.configuration.select_a_project')}
</select> noOptionsMessage={$t('application.configuration.no_projects_found')}
{:else} id="project"
<select name="project" disabled class="w-96"> showIndicator={!loading.projects}
<option disabled selected value="" isWaiting={loading.projects}
>{$t('application.configuration.no_projects_found')}</option isDisabled={loading.projects || !selected.group}
> on:select={selectProject}
</select> on:clear={() => {
{/if} showSave = false;
branches = [];
{#if loading.branches} selected.project = null;
<select name="branch" disabled class="w-96"> selected.branch = null;
<option selected value="">{$t('application.configuration.loading_branches')}</option> }}
</select> value={selected.project}
{:else if !loading.branches && branches.length > 0} isClearable={false}
<select items={projects}
name="branch" loadOptions={searchProjects}
class="w-96" labelIdentifier="name"
bind:value={selected.branch} optionIdentifier="id"
on:change={isBranchAlreadyUsed} />
disabled={!selected.project} </div>
> <div class="custom-select-wrapper">
<option value="" disabled selected>{$t('application.configuration.select_a_branch')}</option <Select
> placeholder={loading.branches
{#each branches as branch} ? $t('application.configuration.loading_branches')
<option value={branch}>{branch.name}</option> : $t('application.configuration.select_a_branch')}
{/each} noOptionsMessage={$t('application.configuration.no_branches_found')}
</select> id="branch"
{:else} showIndicator={!loading.branches}
<select name="project" disabled class="w-96"> isWaiting={loading.branches}
<option disabled selected value="" isDisabled={loading.branches || !selected.project}
>{$t('application.configuration.no_branches_found')}</option on:select={selectBranch}
> on:clear={() => {
</select> showSave = false;
{/if} selected.branch = null;
}}
value={selected.branch}
isClearable={false}
items={branches}
loadOptions={searchBranches}
labelIdentifier="name"
optionIdentifier="web_url"
/>
</div>
</div> </div>
<div class="flex flex-col items-center justify-center space-y-4 pt-5"> <div class="flex flex-col items-center justify-center space-y-4 pt-5">
<button <button

View File

@@ -12,7 +12,7 @@ export const del: RequestHandler = async (event) => {
const database = await db.getDatabase({ id, teamId }); const database = await db.getDatabase({ id, teamId });
if (database.destinationDockerId) { if (database.destinationDockerId) {
const everStarted = await stopDatabase(database); const everStarted = await stopDatabase(database);
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort); if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
} }
await db.removeDatabase({ id }); await db.removeDatabase({ id });
return { status: 200 }; return { status: 200 };

View File

@@ -29,7 +29,7 @@ export const post: RequestHandler = async (event) => {
} }
} else { } else {
await db.prisma.database.update({ where: { id }, data: { publicPort: null } }); await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
await stopTcpHttpProxy(destinationDocker, oldPublicPort); await stopTcpHttpProxy(id, destinationDocker, oldPublicPort);
} }
} }
return { return {

View File

@@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
try { try {
const database = await db.getDatabase({ id, teamId }); const database = await db.getDatabase({ id, teamId });
const everStarted = await stopDatabase(database); const everStarted = await stopDatabase(database);
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort); if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
await db.setDatabase({ id, isPublic: false }); await db.setDatabase({ id, isPublic: false });
await db.prisma.database.update({ where: { id }, data: { publicPort: null } }); await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
@@ -21,6 +21,7 @@ export const post: RequestHandler = async (event) => {
status: 200 status: 200
}; };
} catch (error) { } catch (error) {
console.log(error);
return ErrorHandler(error); return ErrorHandler(error);
} }
}; };

View File

@@ -59,7 +59,7 @@ export const post: RequestHandler = async (event) => {
fider: { fider: {
image: `${image}:${version}`, image: `${image}:${version}`,
environmentVariables: { environmentVariables: {
HOST_DOMAIN: domain, BASE_URL: domain,
DATABASE_URL: `postgresql://${postgresqlUser}:${postgresqlPassword}@${id}-postgresql:5432/${postgresqlDatabase}?sslmode=disable`, DATABASE_URL: `postgresql://${postgresqlUser}:${postgresqlPassword}@${id}-postgresql:5432/${postgresqlDatabase}?sslmode=disable`,
JWT_SECRET: `${jwtSecret.replace(/\$/g, '$$$')}`, JWT_SECRET: `${jwtSecret.replace(/\$/g, '$$$')}`,
EMAIL_NOREPLY: emailNoreply, EMAIL_NOREPLY: emailNoreply,

View File

@@ -7,7 +7,7 @@ import { checkContainer } from '$lib/haproxy';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
function configureMiddleware( function configureMiddleware(
{ id, container, port, domain, nakedDomain, isHttps, isWWW, isDualCerts }, { id, container, port, domain, nakedDomain, isHttps, isWWW, isDualCerts, scriptName, type },
traefik traefik
) { ) {
if (isHttps) { if (isHttps) {
@@ -125,6 +125,15 @@ function configureMiddleware(
} }
} }
} }
if (type === 'plausibleanalytics' && scriptName && scriptName !== 'plausible.js') {
if (!traefik.http.routers[`${id}`].middlewares.includes(`${id}-redir`)) {
traefik.http.routers[`${id}`].middlewares.push(`${id}-redir`);
}
if (!traefik.http.routers[`${id}-secure`].middlewares.includes(`${id}-redir`)) {
traefik.http.routers[`${id}-secure`].middlewares.push(`${id}-redir`);
}
}
} }
export const get: RequestHandler = async (event) => { export const get: RequestHandler = async (event) => {
const traefik = { const traefik = {
@@ -176,7 +185,7 @@ export const get: RequestHandler = async (event) => {
} = application; } = application;
if (destinationDockerId) { if (destinationDockerId) {
const { engine, network } = destinationDocker; const { engine, network } = destinationDocker;
const isRunning = await checkContainer(engine, id); const isRunning = true;
if (fqdn) { if (fqdn) {
const domain = getDomain(fqdn); const domain = getDomain(fqdn);
const nakedDomain = domain.replace(/^www\./, ''); const nakedDomain = domain.replace(/^www\./, '');
@@ -244,7 +253,7 @@ export const get: RequestHandler = async (event) => {
if (found) { if (found) {
const port = found.ports.main; const port = found.ports.main;
const publicPort = service[type]?.publicPort; const publicPort = service[type]?.publicPort;
const isRunning = await checkContainer(engine, id); const isRunning = true;
if (fqdn) { if (fqdn) {
const domain = getDomain(fqdn); const domain = getDomain(fqdn);
const nakedDomain = domain.replace(/^www\./, ''); const nakedDomain = domain.replace(/^www\./, '');
@@ -335,11 +344,6 @@ export const get: RequestHandler = async (event) => {
replacement: '/js/plausible.js' replacement: '/js/plausible.js'
} }
}; };
if (traefik.http.routers[id].middlewares.length > 0) {
traefik.http.routers[id].middlewares.push(`${id}-redir`);
} else {
traefik.http.routers[id].middlewares = [`${id}-redir`];
}
} }
} }
for (const coolify of data.coolify) { for (const coolify of data.coolify) {