This commit is contained in:
Andras Bacsai
2021-06-07 23:44:36 +02:00
committed by GitHub
parent 04a5b1bd4f
commit 9d14b03eb1
36 changed files with 2341 additions and 1169 deletions

View File

@@ -9,9 +9,9 @@ export async function post(request: Request) {
const { DOMAIN } = process.env;
const configuration = setDefaultConfiguration(request.body);
const configurationFound = await Configuration.find({
'repository.id': { '$ne': configuration.repository.id },
'repository.id': { $ne: configuration.repository.id },
'publish.domain': configuration.publish.domain
}).select('-_id -__v -createdAt -updatedAt')
}).select('-_id -__v -createdAt -updatedAt');
if (configurationFound.length > 0 || configuration.publish.domain === DOMAIN) {
return {
status: 200,
@@ -24,7 +24,7 @@ export async function post(request: Request) {
return {
status: 200,
body: { success: true, message: 'OK' }
}
};
} catch (error) {
await saveServerLog(error);
return {

View File

@@ -9,7 +9,7 @@ export async function post(request: Request) {
'repository.name': name,
'repository.organization': organization,
'repository.branch': branch
}).select('-_id -__v -createdAt -updatedAt')
}).select('-_id -__v -createdAt -updatedAt');
if (configurationFound) {
return {

View File

@@ -9,26 +9,40 @@ import type { Request } from '@sveltejs/kit';
export async function post(request: Request) {
const { name, organization, branch, isPreviewDeploymentEnabled }: any = request.body || {};
if (name && organization && branch) {
const configuration = await Configuration.findOneAndUpdate({
'repository.name': name,
'repository.organization': organization,
'repository.branch': branch
}, { $set: { 'general.isPreviewDeploymentEnabled': isPreviewDeploymentEnabled, 'general.pullRequest': 0 } }, { new: true }).select('-_id -__v -createdAt -updatedAt')
const configuration = await Configuration.findOneAndUpdate(
{
'repository.name': name,
'repository.organization': organization,
'repository.branch': branch
},
{
$set: {
'general.isPreviewDeploymentEnabled': isPreviewDeploymentEnabled,
'general.pullRequest': 0
}
},
{ new: true }
).select('-_id -__v -createdAt -updatedAt');
if (!isPreviewDeploymentEnabled) {
const found = await Configuration.find({
'repository.name': name,
'repository.organization': organization,
'repository.branch': branch,
'general.pullRequest': { '$ne': 0 }
})
'general.pullRequest': { $ne: 0 }
});
for (const prDeployment of found) {
await Configuration.findOneAndRemove({
'repository.name': name,
'repository.organization': organization,
'repository.branch': branch,
'publish.domain': prDeployment.publish.domain
})
const deploys = await Deployment.find({ organization, branch, name, domain: prDeployment.publish.domain });
});
const deploys = await Deployment.find({
organization,
branch,
name,
domain: prDeployment.publish.domain
});
for (const deploy of deploys) {
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
await Deployment.deleteMany({ deployId: deploy.deployId });
@@ -51,7 +65,6 @@ export async function post(request: Request) {
success: true
}
};
}
return {
status: 500,

View File

@@ -18,7 +18,9 @@ export async function post(request: Request) {
}
try {
await cloneRepository(configuration);
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(configuration);
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(
configuration
);
if (foundService && !forceUpdate && !imageChanged && !configChanged) {
cleanupTmp(configuration.general.workdir);
return {
@@ -60,15 +62,17 @@ export async function post(request: Request) {
nickname
}).save();
await Configuration.findOneAndUpdate({
'repository.id': id,
'repository.organization': organization,
'repository.name': name,
'repository.branch': branch,
'general.pullRequest': { '$in': [null, 0] },
},
await Configuration.findOneAndUpdate(
{
'repository.id': id,
'repository.organization': organization,
'repository.name': name,
'repository.branch': branch,
'general.pullRequest': { $in: [null, 0] }
},
{ ...configuration },
{ upsert: true, new: true })
{ upsert: true, new: true }
);
queueAndBuild(configuration, imageChanged);
return {
@@ -81,7 +85,7 @@ export async function post(request: Request) {
}
};
} catch (error) {
console.log(error)
console.log(error);
await Deployment.findOneAndUpdate(
{
repoId: configuration.repository.id,

View File

@@ -10,8 +10,7 @@ export async function get(request: Request) {
.select('-_id -__v')
.sort({ createdAt: 'asc' });
const deploy: any = await Deployment.findOne({ deployId })
.select('-_id -__v')
const deploy: any = await Deployment.findOne({ deployId }).select('-_id -__v');
const finalLogs: any = {};
finalLogs.progress = deploy.progress;
finalLogs.events = logs.map((log) => log.event);

View File

@@ -21,7 +21,7 @@ export async function get(request: Request) {
const updatedAt = dayjs(d.updatedAt).utc();
finalLogs.took = updatedAt.diff(dayjs(d.createdAt)) / 1000;
finalLogs.since = updatedAt.fromNow();
finalLogs.isPr = d.domain.startsWith('pr')
finalLogs.isPr = d.domain.startsWith('pr');
return finalLogs;
});
return {

View File

@@ -16,7 +16,7 @@ export async function get(request: Request) {
body: { success: true, logs }
};
} catch (error) {
console.log(error)
console.log(error);
await saveServerLog(error);
return {
status: 500,

View File

@@ -16,25 +16,25 @@ export async function post(request: Request) {
'repository.name': name,
'repository.branch': branch,
'publish.domain': domain
})
});
if (configurationFound) {
const id = configurationFound._id
const id = configurationFound._id;
if (configurationFound?.general?.pullRequest === 0) {
// Main deployment deletion request; deleting main + PRs
const allConfiguration = await Configuration.find({
'repository.name': name,
'repository.organization': organization,
'repository.branch': branch,
})
'repository.branch': branch
});
for (const config of allConfiguration) {
await Configuration.findOneAndRemove({
'repository.name': config.repository.name,
'repository.organization': config.repository.organization,
'repository.branch': config.repository.branch,
})
'repository.branch': config.repository.branch
});
await execShellAsync(`docker stack rm ${config.build.container.name}`);
}
const deploys = await Deployment.find({ organization, branch, name })
const deploys = await Deployment.find({ organization, branch, name });
for (const deploy of deploys) {
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
await Deployment.deleteMany({ deployId: deploy.deployId });
@@ -43,9 +43,9 @@ export async function post(request: Request) {
purgeImagesAsync(configurationFound);
} else {
// Delete only PRs
await Configuration.findByIdAndRemove(id)
await Configuration.findByIdAndRemove(id);
await execShellAsync(`docker stack rm ${configurationFound.build.container.name}`);
const deploys = await Deployment.find({ organization, branch, name, domain })
const deploys = await Deployment.find({ organization, branch, name, domain });
for (const deploy of deploys) {
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
await Deployment.deleteMany({ deployId: deploy.deployId });
@@ -63,7 +63,7 @@ export async function post(request: Request) {
}
};
} catch (error) {
console.log(error)
console.log(error);
return {
status: 500,
error: {

View File

@@ -1,6 +1,6 @@
import { docker } from '$lib/api/docker';
import type { Request } from '@sveltejs/kit';
import Configuration from '$models/Configuration'
import Configuration from '$models/Configuration';
export async function get(request: Request) {
// Should update this to get data from mongodb and update db with the currently running services on start!
const dockerServices = await docker.engine.listServices();
@@ -34,21 +34,21 @@ export async function get(request: Request) {
return {};
});
const configurations = await Configuration.find({
'general.pullRequest': { '$in': [null, 0] }
}).select('-_id -__v -createdAt')
const applications = []
'general.pullRequest': { $in: [null, 0] }
}).select('-_id -__v -createdAt');
const applications = [];
for (const configuration of configurations) {
const foundPRDeployments = await Configuration.find({
'repository.id': configuration.repository.id,
'repository.branch': configuration.repository.branch,
'general.pullRequest': { '$ne': 0 }
}).select('-_id -__v -createdAt')
'general.pullRequest': { $ne: 0 }
}).select('-_id -__v -createdAt');
const payload = {
configuration,
UpdatedAt: configuration.updatedAt,
prBuilds: foundPRDeployments.length > 0 ? true : false,
}
applications.push(payload)
prBuilds: foundPRDeployments.length > 0 ? true : false
};
applications.push(payload);
}
return {
status: 200,

View File

@@ -4,6 +4,7 @@ import type { Request } from '@sveltejs/kit';
export async function get(request: Request) {
const { serviceName } = request.params;
try {
const service = (await docker.engine.listServices()).find(
(r) =>

View File

@@ -0,0 +1,59 @@
import type { Request } from '@sveltejs/kit';
import yaml from 'js-yaml';
import { promises as fs } from 'fs';
import { docker } from '$lib/api/docker';
import { baseServiceConfiguration } from '$lib/api/applications/common';
import { cleanupTmp, execShellAsync } from '$lib/api/common';
export async function post(request: Request) {
let { baseURL } = request.body;
const traefikURL = baseURL;
baseURL = `https://${baseURL}`;
const workdir = '/tmp/nocodb';
const deployId = 'nocodb';
const stack = {
version: '3.8',
services: {
[deployId]: {
image: 'nocodb/nocodb',
networks: [`${docker.network}`],
deploy: {
...baseServiceConfiguration,
labels: [
'managedBy=coolify',
'type=service',
'serviceName=nocodb',
'configuration=' +
JSON.stringify({
baseURL
}),
'traefik.enable=true',
'traefik.http.services.' + deployId + '.loadbalancer.server.port=8080',
'traefik.http.routers.' + deployId + '.entrypoints=websecure',
'traefik.http.routers.' +
deployId +
'.rule=Host(`' +
traefikURL +
'`) && PathPrefix(`/`)',
'traefik.http.routers.' + deployId + '.tls.certresolver=letsencrypt',
'traefik.http.routers.' + deployId + '.middlewares=global-compress'
]
}
}
},
networks: {
[`${docker.network}`]: {
external: true
}
}
};
await execShellAsync(`mkdir -p ${workdir}`);
await fs.writeFile(`${workdir}/stack.yml`, yaml.dump(stack));
await execShellAsync('docker stack rm nocodb');
await execShellAsync(`cat ${workdir}/stack.yml | docker stack deploy --prune -c - ${deployId}`);
cleanupTmp(workdir);
return {
status: 200,
body: { message: 'OK' }
};
}

View File

@@ -11,9 +11,9 @@ import ApplicationLog from '$models/ApplicationLog';
import { cleanupStuckedDeploymentsInDB } from '$lib/api/applications/cleanup';
export async function post(request: Request) {
let configuration;
const allowedGithubEvents = ['push', 'pull_request']
const allowedPRActions = ['opened', , 'reopened', 'synchronize', 'closed']
const githubEvent = request.headers['x-github-event']
const allowedGithubEvents = ['push', 'pull_request'];
const allowedPRActions = ['opened', 'reopened', 'synchronize', 'closed'];
const githubEvent = request.headers['x-github-event'];
const { GITHUP_APP_WEBHOOK_SECRET } = process.env;
const hmac = crypto.createHmac('sha256', GITHUP_APP_WEBHOOK_SECRET);
const digest = Buffer.from(
@@ -41,8 +41,8 @@ export async function post(request: Request) {
try {
const applications = await Configuration.find({
'repository.id': request.body.repository.id,
}).select('-_id -__v -createdAt -updatedAt')
'repository.id': request.body.repository.id
}).select('-_id -__v -createdAt -updatedAt');
if (githubEvent === 'push') {
configuration = applications.find((r) => {
if (request.body.ref.startsWith('refs')) {
@@ -61,7 +61,9 @@ export async function post(request: Request) {
}
};
}
configuration = applications.find((r) => r.repository.branch === request.body['pull_request'].base.ref);
configuration = applications.find(
(r) => r.repository.branch === request.body['pull_request'].base.ref
);
if (configuration) {
if (!configuration.general.isPreviewDeploymentEnabled) {
return {
@@ -71,7 +73,7 @@ export async function post(request: Request) {
}
};
}
configuration.general.pullRequest = request.body.number
configuration.general.pullRequest = request.body.number;
}
}
if (!configuration) {
@@ -99,7 +101,7 @@ export async function post(request: Request) {
'repository.name': name,
'repository.branch': branch,
'general.pullRequest': pullRequest
})
});
await execShellAsync(`docker stack rm ${configuration.build.container.name}`);
return {
status: 200,
@@ -110,7 +112,9 @@ export async function post(request: Request) {
};
}
await cloneRepository(configuration);
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(configuration);
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(
configuration
);
if (foundService && !forceUpdate && !imageChanged && !configChanged) {
cleanupTmp(configuration.general.workdir);
return {
@@ -149,27 +153,30 @@ export async function post(request: Request) {
nickname
}).save();
if (githubEvent === 'pull_request') {
await Configuration.findOneAndUpdate({
'repository.id': id,
'repository.organization': organization,
'repository.name': name,
'repository.branch': branch,
'general.pullRequest': pullRequest
},
await Configuration.findOneAndUpdate(
{
'repository.id': id,
'repository.organization': organization,
'repository.name': name,
'repository.branch': branch,
'general.pullRequest': pullRequest
},
{ ...configuration },
{ upsert: true, new: true })
{ upsert: true, new: true }
);
} else {
await Configuration.findOneAndUpdate({
'repository.id': id,
'repository.organization': organization,
'repository.name': name,
'repository.branch': branch,
'general.pullRequest': { '$in': [null, 0] }
},
await Configuration.findOneAndUpdate(
{
'repository.id': id,
'repository.organization': organization,
'repository.name': name,
'repository.branch': branch,
'general.pullRequest': { $in: [null, 0] }
},
{ ...configuration },
{ upsert: true, new: true })
{ upsert: true, new: true }
);
}
queueAndBuild(configuration, imageChanged);
@@ -183,7 +190,7 @@ export async function post(request: Request) {
}
};
} catch (error) {
console.log(error)
console.log(error);
// console.log(configuration)
if (configuration) {
cleanupTmp(configuration.general.workdir);
@@ -216,7 +223,7 @@ export async function post(request: Request) {
try {
await cleanupStuckedDeploymentsInDB();
} catch (error) {
console.log(error)
console.log(error);
}
}
}

View File

@@ -49,6 +49,15 @@
/>
<div class="text-white font-bold">Plausible Analytics</div>
</div>
{:else if service.serviceName == 'nocodb'}
<div>
<img
alt="nocodedb"
class="w-10 absolute top-0 left-0 -m-6"
src="https://cdn.coollabs.io/assets/coolify/services/nocodb/nocodb.png"
/>
<div class="text-white font-bold">NocoDB</div>
</div>
{/if}
</div>
</div>

View File

@@ -14,23 +14,14 @@
try {
service = await request(`/api/v1/services/${$page.params.name}`, $session);
} catch (error) {
browser && toast.push(`Cannot find service ${$page.params.name}?!`);
goto(`/dashboard/services`, { replaceState: true });
if (browser) {
toast.push(`Cannot find service ${$page.params.name}?!`);
goto(`/dashboard/services`, { replaceState: true });
}
}
}
}
async function activate() {
try {
await request(`/api/v1/services/deploy/${$page.params.name}/activate`, $session, {
method: 'PATCH',
body: {}
});
browser && toast.push(`All users are activated for Plausible.`);
} catch (error) {
console.log(error);
browser && toast.push(`Ooops, there was an error activating users for Plausible?!`);
}
}
</script>
{#await loadServiceConfig()}
@@ -38,7 +29,12 @@
{:then}
<div class="min-h-full text-white">
<div class="py-5 text-left px-6 text-3xl tracking-tight font-bold flex items-center">
<div>{$page.params.name === 'plausible' ? 'Plausible Analytics' : $page.params.name}</div>
{#if $page.params.name === 'plausible'}
<div>Plausible Analytics</div>
{:else if $page.params.name === 'nocodb'}
<div>NocoDB</div>
{/if}
<div class="px-4">
{#if $page.params.name === 'plausible'}
<img
@@ -46,37 +42,38 @@
class="w-6 mx-auto"
src="https://cdn.coollabs.io/assets/coolify/services/plausible/logo_sm.png"
/>
{:else if $page.params.name === 'nocodb'}
<img
alt="nocodb logo"
class="w-8 mx-auto"
src="https://cdn.coollabs.io/assets/coolify/services/nocodb/nocodb.png"
/>
{/if}
</div>
<a
target="_blank"
class="icon mx-2"
href={service.config.baseURL}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
<a target="_blank" class="icon mx-2" href={service.config.baseURL}>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg></a
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg></a
>
</div>
</div>
<div class="space-y-2 max-w-4xl mx-auto px-6" in:fade={{ duration: 100 }}>
<div class="block text-center py-4">
{#if $page.params.name === 'plausible'}
<Plausible {service} />
{:else if $page.params.name === 'nocodb'}
<div class="font-bold">Nothing to show here. Enjoy using NocoDB!</div>
{/if}
</div>
</div>

View File

@@ -14,13 +14,11 @@
async function checkService() {
try {
const data = await request(`/api/v1/services/${$page.params.type}`, $session);
if (!data?.success) {
if (data?.success) {
if (browser) {
goto(`/dashboard/services`, { replaceState: true });
goto(`/service/${$page.params.type}/configuration`, { replaceState: true });
toast.push(
`${
$page.params.type === 'plausible' ? 'Plausible Analytics' : $page.params.type
} already deployed.`
`Service already deployed.`
);
}
}

View File

@@ -10,7 +10,7 @@
import TooltipInfo from '$components/TooltipInfo.svelte';
import { browser } from '$app/env';
$: deployable =
$: deployablePlausible =
$newService.baseURL === '' ||
$newService.baseURL === null ||
$newService.email === '' ||
@@ -22,7 +22,7 @@
$newService.userPassword.length <= 6 ||
$newService.userPassword !== $newService.userPasswordAgain;
let loading = false;
async function deploy() {
async function deployPlausible() {
try {
loading = true;
const payload = $newService;
@@ -44,6 +44,30 @@
loading = false;
}
}
async function deployNocodb() {
try {
loading = true;
await request(`/api/v1/services/deploy/${$page.params.type}`, $session, {
body: {
baseURL: $newService.baseURL
}
});
if (browser) {
toast.push(
'Service deployment queued.<br><br><br>It could take 2-5 minutes to be ready, be patient and grab a coffee/tea!',
{ duration: 4000 }
);
goto(`/dashboard/services`, { replaceState: true });
}
} catch (error) {
console.log(error);
browser && toast.push('Oops something went wrong. See console.log.');
} finally {
loading = false;
}
}
</script>
<div class="min-h-full text-white">
@@ -51,18 +75,20 @@
Deploy new
{#if $page.params.type === 'plausible'}
<span class="text-blue-500 px-2 capitalize">Plausible Analytics</span>
{:else if $page.params.type === 'nocodb'}
<span class="text-blue-500 px-2 capitalize">NocoDB</span>
{/if}
</div>
</div>
{#if loading}
<Loading />
{:else}
{:else if $page.params.type === 'plausible'}
<div class="space-y-2 max-w-4xl mx-auto px-6 flex-col text-center" in:fade={{ duration: 100 }}>
<div class="grid grid-flow-row">
<label for="Domain"
>Domain <TooltipInfo
position="right"
label={`You will have your Plausible instance at here.`}
label={`You could reach your Plausible Analytics instance here.`}
/></label
>
<input
@@ -114,15 +140,39 @@
/>
</div>
<button
disabled={deployable}
class:cursor-not-allowed={deployable}
class:bg-blue-500={!deployable}
class:hover:bg-blue-400={!deployable}
class:hover:bg-transparent={deployable}
class:text-warmGray-700={deployable}
class:text-white={!deployable}
disabled={deployablePlausible}
class:cursor-not-allowed={deployablePlausible}
class:bg-blue-500={!deployablePlausible}
class:hover:bg-blue-400={!deployablePlausible}
class:hover:bg-transparent={deployablePlausible}
class:text-warmGray-700={deployablePlausible}
class:text-white={!deployablePlausible}
class="button p-2"
on:click={deploy}
on:click={deployPlausible}
>
Deploy
</button>
</div>
{:else if $page.params.type === 'nocodb'}
<div class="space-y-2 max-w-xl mx-auto px-6 flex-col text-center" in:fade={{ duration: 100 }}>
<div class="grid grid-flow-row pb-5">
<label for="Domain"
>Domain <TooltipInfo
position="right"
label={`You could reach your NocoDB instance here.`}
/></label
>
<input
id="Domain"
class:border-red-500={$newService.baseURL == null || $newService.baseURL == ''}
bind:value={$newService.baseURL}
placeholder="nocodb.coollabs.io"
/>
</div>
<button
class="button p-2 w-64 bg-blue-500 hover:bg-blue-400 text-white"
on:click={deployNocodb}
>
Deploy
</button>

View File

@@ -1,7 +1,6 @@
<script>
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { fade } from 'svelte/transition';
</script>
@@ -14,7 +13,7 @@
{#if $page.path === '/service/new'}
<div class="flex justify-center space-x-4 font-bold pb-6">
<div
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-500 p-2 rounded bg-warmGray-800"
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-500 p-2 rounded bg-warmGray-800 w-48"
on:click={() => goto('/service/new/plausible')}
>
<img
@@ -24,6 +23,18 @@
/>
<div class="text-white">Plausible Analytics</div>
</div>
<div
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-white p-2 rounded bg-warmGray-800 w-48"
on:click={() => goto('/service/new/nocodb')}
>
<img
alt="nocodb logo"
class="w-14 mx-auto pb-2"
src="https://cdn.coollabs.io/assets/coolify/services/nocodb/nocodb.png"
/>
<div class="flex-1" />
<div class="text-white">NocoDB</div>
</div>
</div>
{/if}
</div>