mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-20 20:59:23 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ff9c5fed5 | ||
|
|
d43cd663d2 | ||
|
|
dae91267e8 | ||
|
|
b2d6317a23 | ||
|
|
c49b412e69 | ||
|
|
05e5d73556 | ||
|
|
53620f4b1a | ||
|
|
9d14b03eb1 |
@@ -16,7 +16,8 @@ Installation is automated with the following command:
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
You can deploy any of the following applications, databases and services easily.
|
- Deploy Pull Request automatically, so you can review them quickly!
|
||||||
|
- You can deploy any of the following applications, databases and services easily.
|
||||||
|
|
||||||
(constantly growing lists)
|
(constantly growing lists)
|
||||||
|
|
||||||
@@ -47,7 +48,12 @@ With Github integration
|
|||||||
|
|
||||||
### Services
|
### Services
|
||||||
|
|
||||||
|
- [WordPress](https://wordpress.org)
|
||||||
- [Plausible Analytics](https://plausible.io)
|
- [Plausible Analytics](https://plausible.io)
|
||||||
|
- [NocoDB](https://nocodb.com)
|
||||||
|
- [VSCode Server](https://github.com/cdr/code-server)
|
||||||
|
- [MinIO](https://min.io)
|
||||||
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
|||||||
35
package.json
35
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "coolify",
|
"name": "coolify",
|
||||||
"description": "An open-source, hassle-free, self-hostable Heroku & Netlify alternative.",
|
"description": "An open-source, hassle-free, self-hostable Heroku & Netlify alternative.",
|
||||||
"version": "1.0.16",
|
"version": "1.0.20",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:docker:start": "docker-compose -f docker-compose-dev.yml up -d",
|
"dev:docker:start": "docker-compose -f docker-compose-dev.yml up -d",
|
||||||
@@ -14,27 +14,26 @@
|
|||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-node": "^1.0.0-next.24",
|
"@sveltejs/adapter-node": "1.0.0-next.26",
|
||||||
"@sveltejs/kit": "1.0.0-next.113",
|
"@sveltejs/kit": "1.0.0-next.115",
|
||||||
"@types/dockerode": "^3.2.3",
|
"@types/dockerode": "^3.2.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
"@typescript-eslint/eslint-plugin": "^4.26.1",
|
||||||
"@typescript-eslint/parser": "^4.23.0",
|
"@typescript-eslint/parser": "^4.26.1",
|
||||||
"autoprefixer": "^10.2.5",
|
"autoprefixer": "^10.2.6",
|
||||||
"cssnano": "^5.0.2",
|
"cssnano": "^5.0.5",
|
||||||
"dotenv-extended": "^2.9.0",
|
"eslint": "^7.28.0",
|
||||||
"eslint": "^7.26.0",
|
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-svelte3": "^3.2.0",
|
"eslint-plugin-svelte3": "^3.2.0",
|
||||||
"postcss": "^8.2.15",
|
"postcss": "^8.3.0",
|
||||||
"postcss-load-config": "^3.0.1",
|
"postcss-load-config": "^3.0.1",
|
||||||
"prettier": "~2.3.0",
|
"prettier": "~2.3.1",
|
||||||
"prettier-plugin-svelte": "^2.3.0",
|
"prettier-plugin-svelte": "^2.3.0",
|
||||||
"svelte": "^3.38.2",
|
"svelte": "^3.38.2",
|
||||||
"svelte-preprocess": "^4.7.3",
|
"svelte-preprocess": "^4.7.3",
|
||||||
"tailwindcss": "2.2.0-canary.8",
|
"tailwindcss": "2.2.0",
|
||||||
"tslib": "^2.2.0",
|
"tslib": "^2.2.0",
|
||||||
"typescript": "^4.2.4",
|
"typescript": "^4.3.2",
|
||||||
"vite": "^2.3.2"
|
"vite": "^2.3.6"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -44,15 +43,17 @@
|
|||||||
"compare-versions": "^3.6.0",
|
"compare-versions": "^3.6.0",
|
||||||
"cookie": "^0.4.1",
|
"cookie": "^0.4.1",
|
||||||
"cuid": "^2.1.8",
|
"cuid": "^2.1.8",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.5",
|
||||||
"dockerode": "^3.3.0",
|
"dockerode": "^3.3.0",
|
||||||
|
"dotenv-extended": "^2.9.0",
|
||||||
"generate-password": "^1.6.0",
|
"generate-password": "^1.6.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"mongoose": "^5.12.9",
|
"mongoose": "^5.12.13",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.4",
|
||||||
"svelte-kit-cookie-session": "^0.4.3",
|
"svelte-kit-cookie-session": "^1.0.6",
|
||||||
"svelte-select": "^3.17.0",
|
"svelte-select": "^3.17.0",
|
||||||
|
"systeminformation": "^5.7.7",
|
||||||
"unique-names-generator": "^4.5.0"
|
"unique-names-generator": "^4.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
967
pnpm-lock.yaml
generated
967
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -92,7 +92,9 @@ body {
|
|||||||
input {
|
input {
|
||||||
@apply text-sm rounded py-2 px-6 font-bold bg-warmGray-800 text-white transition duration-150 outline-none border border-transparent !important;
|
@apply text-sm rounded py-2 px-6 font-bold bg-warmGray-800 text-white transition duration-150 outline-none border border-transparent !important;
|
||||||
}
|
}
|
||||||
input:hover {
|
input:hover,
|
||||||
|
input:focus-visible,
|
||||||
|
input:focus {
|
||||||
@apply bg-warmGray-700 !important;
|
@apply bg-warmGray-700 !important;
|
||||||
}
|
}
|
||||||
textarea {
|
textarea {
|
||||||
@@ -107,6 +109,7 @@ select {
|
|||||||
select:hover {
|
select:hover {
|
||||||
@apply bg-warmGray-700 !important;
|
@apply bg-warmGray-700 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
@apply text-left text-base font-bold text-warmGray-400 !important;
|
@apply text-left text-base font-bold text-warmGray-400 !important;
|
||||||
}
|
}
|
||||||
@@ -116,6 +119,11 @@ button {
|
|||||||
.button {
|
.button {
|
||||||
@apply rounded text-sm font-bold transition-all duration-100 !important;
|
@apply rounded text-sm font-bold transition-all duration-100 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button:focus-visible,
|
||||||
|
.button:focus {
|
||||||
|
@apply bg-warmGray-700 !important;
|
||||||
|
}
|
||||||
.h-271 {
|
.h-271 {
|
||||||
min-height: 271px !important;
|
min-height: 271px !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,7 +339,7 @@
|
|||||||
>
|
>
|
||||||
<span class="sr-only">Use setting</span>
|
<span class="sr-only">Use setting</span>
|
||||||
<span
|
<span
|
||||||
class="pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"
|
class="pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transition ease-in-out duration-200 transform"
|
||||||
class:translate-x-5={$application.general.isPreviewDeploymentEnabled}
|
class:translate-x-5={$application.general.isPreviewDeploymentEnabled}
|
||||||
class:translate-x-0={!$application.general.isPreviewDeploymentEnabled}
|
class:translate-x-0={!$application.general.isPreviewDeploymentEnabled}
|
||||||
>
|
>
|
||||||
@@ -588,7 +588,7 @@
|
|||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.buildpack {
|
.buildpack {
|
||||||
@apply px-6 py-2 mx-2 my-2 bg-warmGray-800 w-48 ease-in-out transform hover:scale-105 text-center rounded border-2 border-transparent border-dashed cursor-pointer transition duration-100;
|
@apply px-6 py-2 mx-2 my-2 bg-warmGray-800 w-48 ease-in-out hover:scale-105 text-center rounded border-2 border-transparent border-dashed cursor-pointer transition duration-100;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (installations.length === 0) {
|
if (installations.length === 0) {
|
||||||
relogin = true;
|
loading.github = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$application.github.installation.id = installations[0].id;
|
$application.github.installation.id = installations[0].id;
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
}
|
}
|
||||||
async function loadPermissions() {
|
async function loadPermissions() {
|
||||||
const config = await request(
|
const config = await request(
|
||||||
`https://api.github.com/apps/${import.meta.env.VITE_GITHUB_APP_NAME}`,
|
`https://api.github.com/apps/${dashify(import.meta.env.VITE_GITHUB_APP_NAME)}`,
|
||||||
$session
|
$session
|
||||||
);
|
);
|
||||||
if (config.permissions['pull_requests'] && config.events.includes('pull_request')) {
|
if (config.permissions['pull_requests'] && config.events.includes('pull_request')) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<div class="text-center text-white">
|
<div class="text-center text-white">
|
||||||
<div class="text-2xl font-bold text-center pb-4">Choose your Git provider</div>
|
<div class="text-2xl font-bold text-center pb-4">Choose your Git provider</div>
|
||||||
<button on:click={login} class="hover:scale-110 transform duration-100 transition">
|
<button on:click={login} class="hover:scale-110 duration-100 transition">
|
||||||
<svg
|
<svg
|
||||||
class="w-16"
|
class="w-16"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
{#if $page.path === '/database/new'}
|
{#if $page.path === '/database/new'}
|
||||||
<div class="flex justify-center space-x-4 font-bold pb-6">
|
<div class="flex justify-center space-x-4 font-bold pb-6">
|
||||||
<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-green-600 p-2 rounded bg-warmGray-800 w-32"
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-green-600 p-2 rounded bg-warmGray-800 w-32"
|
||||||
class:border-green-600={type === 'mongodb'}
|
class:border-green-600={type === 'mongodb'}
|
||||||
on:click={() => (type = 'mongodb')}
|
on:click={() => (type = 'mongodb')}
|
||||||
>
|
>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
<div class="text-white">MongoDB</div>
|
<div class="text-white">MongoDB</div>
|
||||||
</div>
|
</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-red-600 p-2 rounded bg-warmGray-800 w-32"
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-red-600 p-2 rounded bg-warmGray-800 w-32"
|
||||||
class:border-red-600={type === 'couchdb'}
|
class:border-red-600={type === 'couchdb'}
|
||||||
on:click={() => (type = 'couchdb')}
|
on:click={() => (type = 'couchdb')}
|
||||||
>
|
>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
<div class="text-white">Couchdb</div>
|
<div class="text-white">Couchdb</div>
|
||||||
</div>
|
</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-blue-600 p-2 rounded bg-warmGray-800 w-32"
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-600 p-2 rounded bg-warmGray-800 w-32"
|
||||||
class:border-blue-600={type === 'postgresql'}
|
class:border-blue-600={type === 'postgresql'}
|
||||||
on:click={() => (type = 'postgresql')}
|
on:click={() => (type = 'postgresql')}
|
||||||
>
|
>
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
<div class="text-white">PostgreSQL</div>
|
<div class="text-white">PostgreSQL</div>
|
||||||
</div>
|
</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-orange-600 p-2 rounded bg-warmGray-800 w-32"
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-orange-600 p-2 rounded bg-warmGray-800 w-32"
|
||||||
class:border-orange-600={type === 'mysql'}
|
class:border-orange-600={type === 'mysql'}
|
||||||
on:click={() => (type = 'mysql')}
|
on:click={() => (type = 'mysql')}
|
||||||
>
|
>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
<div class="text-white">MySQL</div>
|
<div class="text-white">MySQL</div>
|
||||||
</div>
|
</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-red-600 p-2 rounded bg-warmGray-800 w-32"
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-red-600 p-2 rounded bg-warmGray-800 w-32"
|
||||||
class:border-red-600={type === 'redis'}
|
class:border-red-600={type === 'redis'}
|
||||||
on:click={() => (type = 'redis')}
|
on:click={() => (type = 'redis')}
|
||||||
>
|
>
|
||||||
|
|||||||
35
src/components/Service/CodeServer.svelte
Normal file
35
src/components/Service/CodeServer.svelte
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<script>
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
|
import Loading from '../Loading.svelte';
|
||||||
|
import Tooltip from '$components/Tooltip.svelte';
|
||||||
|
import { request } from '$lib/request';
|
||||||
|
import { page, session } from '$app/stores';
|
||||||
|
import PasswordField from '$components/PasswordField.svelte';
|
||||||
|
import { browser } from '$app/env';
|
||||||
|
export let service;
|
||||||
|
|
||||||
|
async function getPassword() {
|
||||||
|
try {
|
||||||
|
const { password } = await request(`/api/v1/services/deploy/${$page.params.name}/password`, $session);
|
||||||
|
service.config.password = password
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
browser && toast.push(`Ooops, there was an error activating users for VSCode Server?!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await getPassword()}
|
||||||
|
<Loading />
|
||||||
|
{:then}
|
||||||
|
<div class="text-left max-w-5xl mx-auto px-6" in:fade={{ duration: 100 }}>
|
||||||
|
<div class="pb-2 pt-5 space-y-4">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="font-bold w-64 text-warmGray-400">Password</div>
|
||||||
|
<PasswordField value={service.config.password} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
26
src/components/Service/MinIO.svelte
Normal file
26
src/components/Service/MinIO.svelte
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<script>
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
|
import Loading from '../Loading.svelte';
|
||||||
|
import Tooltip from '$components/Tooltip.svelte';
|
||||||
|
import { request } from '$lib/request';
|
||||||
|
import { page, session } from '$app/stores';
|
||||||
|
import PasswordField from '$components/PasswordField.svelte';
|
||||||
|
import { browser } from '$app/env';
|
||||||
|
export let service;
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="text-left max-w-5xl mx-auto px-6" in:fade={{ duration: 100 }}>
|
||||||
|
<div class="pb-2 pt-5 space-y-4">
|
||||||
|
<div class="flex items-center pt-4">
|
||||||
|
<div class="font-bold w-64 text-warmGray-400">Root User</div>
|
||||||
|
<input class="w-full" value={service.config.generateEnvsMinIO.MINIO_ROOT_USER} disabled />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="font-bold w-64 text-warmGray-400">Root Password</div>
|
||||||
|
<PasswordField value={service.config.generateEnvsMinIO.MINIO_ROOT_PASSWORD} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
import { request } from '$lib/request';
|
import { request } from '$lib/request';
|
||||||
import { page, session } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import PasswordField from '$components/PasswordField.svelte';
|
import PasswordField from '$components/PasswordField.svelte';
|
||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
export let service;
|
export let service;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
async function activate() {
|
async function activate() {
|
||||||
@@ -24,6 +24,7 @@ import { browser } from '$app/env';
|
|||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if loading}
|
{#if loading}
|
||||||
|
|||||||
3
src/global.d.ts
vendored
3
src/global.d.ts
vendored
@@ -49,8 +49,7 @@ export type Application = {
|
|||||||
python: {
|
python: {
|
||||||
module?: string;
|
module?: string;
|
||||||
instance?: string;
|
instance?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
container: {
|
container: {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -31,11 +31,9 @@ async function connectMongoDB() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
console.log('Connected to mongodb.');
|
console.log('Connected to mongodb.');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -48,7 +46,7 @@ async function connectMongoDB() {
|
|||||||
try {
|
try {
|
||||||
await cleanupStuckedDeploymentsInDB();
|
await cleanupStuckedDeploymentsInDB();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const dockerServices = await docker.engine.listServices();
|
const dockerServices = await docker.engine.listServices();
|
||||||
@@ -76,20 +74,23 @@ async function connectMongoDB() {
|
|||||||
).values()
|
).values()
|
||||||
];
|
];
|
||||||
for (const application of applications) {
|
for (const application of applications) {
|
||||||
await Configuration.findOneAndUpdate({
|
await Configuration.findOneAndUpdate(
|
||||||
'repository.name': application.configuration.repository.name,
|
{
|
||||||
'repository.organization': application.configuration.repository.organization,
|
'repository.name': application.configuration.repository.name,
|
||||||
'repository.branch': application.configuration.repository.branch,
|
'repository.organization': application.configuration.repository.organization,
|
||||||
'publish.domain': application.configuration.publish.domain
|
'repository.branch': application.configuration.repository.branch,
|
||||||
}, {
|
'publish.domain': application.configuration.publish.domain
|
||||||
...application.configuration
|
},
|
||||||
}, { upsert: true, new: true })
|
{
|
||||||
|
...application.configuration
|
||||||
|
},
|
||||||
|
{ upsert: true, new: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
})()
|
})();
|
||||||
|
|
||||||
|
|
||||||
export async function handle({ request, resolve }) {
|
export async function handle({ request, resolve }) {
|
||||||
const { SECRETS_ENCRYPTION_KEY } = process.env;
|
const { SECRETS_ENCRYPTION_KEY } = process.env;
|
||||||
@@ -99,7 +100,8 @@ export async function handle({ request, resolve }) {
|
|||||||
secret: SECRETS_ENCRYPTION_KEY,
|
secret: SECRETS_ENCRYPTION_KEY,
|
||||||
cookie: { path: '/' }
|
cookie: { path: '/' }
|
||||||
});
|
});
|
||||||
} catch(error) {
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
return {
|
return {
|
||||||
status: 302,
|
status: 302,
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export async function deleteSameDeployments(configuration) {
|
|||||||
const running = JSON.parse(s.Spec.Labels.configuration);
|
const running = JSON.parse(s.Spec.Labels.configuration);
|
||||||
if (
|
if (
|
||||||
running.repository.id === configuration.repository.id &&
|
running.repository.id === configuration.repository.id &&
|
||||||
running.repository.branch === configuration.repository.branch &&
|
running.repository.branch === configuration.repository.branch &&
|
||||||
running.publish.domain === configuration.publish.domain
|
running.publish.domain === configuration.publish.domain
|
||||||
) {
|
) {
|
||||||
await execShellAsync(`docker stack rm ${s.Spec.Labels['com.docker.stack.namespace']}`);
|
await execShellAsync(`docker stack rm ${s.Spec.Labels['com.docker.stack.namespace']}`);
|
||||||
@@ -55,7 +55,6 @@ export async function purgeImagesContainers(configuration, deleteAll = false) {
|
|||||||
.split('\n');
|
.split('\n');
|
||||||
if (IDsToDelete.length > 1) await execShellAsync(`docker rmi -f ${IDsToDelete.join(' ')}`);
|
if (IDsToDelete.length > 1) await execShellAsync(`docker rmi -f ${IDsToDelete.join(' ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ export default async function (configuration) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (isPreviewDeploymentEnabled && pullRequest && pullRequest !== 0) {
|
if (isPreviewDeploymentEnabled && pullRequest && pullRequest !== 0) {
|
||||||
await execShellAsync(`cd ${workdir} && git fetch origin pull/${pullRequest}/head:pull_${pullRequest} && git checkout pull_${pullRequest}`)
|
await execShellAsync(
|
||||||
|
`cd ${workdir} && git fetch origin pull/${pullRequest}/head:pull_${pullRequest} && git checkout pull_${pullRequest}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
configuration.build.container.tag = (
|
configuration.build.container.tag = (
|
||||||
await execShellAsync(`cd ${workdir}/ && git rev-parse HEAD`)
|
await execShellAsync(`cd ${workdir}/ && git rev-parse HEAD`)
|
||||||
|
|||||||
@@ -21,8 +21,11 @@ export function setDefaultConfiguration(configuration) {
|
|||||||
configuration.general.deployId = deployId;
|
configuration.general.deployId = deployId;
|
||||||
configuration.general.workdir = `/tmp/${deployId}`;
|
configuration.general.workdir = `/tmp/${deployId}`;
|
||||||
if (configuration.general.isPreviewDeploymentEnabled && configuration.general.pullRequest !== 0) {
|
if (configuration.general.isPreviewDeploymentEnabled && configuration.general.pullRequest !== 0) {
|
||||||
configuration.build.container.name = `pr${configuration.general.pullRequest}-${sha256.slice(0, 8)}`
|
configuration.build.container.name = `pr${configuration.general.pullRequest}-${sha256.slice(
|
||||||
configuration.publish.domain = `pr${configuration.general.pullRequest}.${configuration.publish.domain}`
|
0,
|
||||||
|
8
|
||||||
|
)}`;
|
||||||
|
configuration.publish.domain = `pr${configuration.general.pullRequest}.${configuration.publish.domain}`;
|
||||||
}
|
}
|
||||||
if (!configuration.publish.path) configuration.publish.path = '/';
|
if (!configuration.publish.path) configuration.publish.path = '/';
|
||||||
if (!configuration.publish.port) {
|
if (!configuration.publish.port) {
|
||||||
@@ -59,11 +62,13 @@ export function setDefaultConfiguration(configuration) {
|
|||||||
configuration.build.pack === 'nextjs' ||
|
configuration.build.pack === 'nextjs' ||
|
||||||
configuration.build.pack === 'nestjs'
|
configuration.build.pack === 'nestjs'
|
||||||
) {
|
) {
|
||||||
if (!configuration.build.command.start) configuration.build.command.start = 'yarn start'
|
if (!configuration.build.command.start) configuration.build.command.start = 'yarn start';
|
||||||
}
|
}
|
||||||
if (configuration.build.pack === 'python') {
|
if (configuration.build.pack === 'python') {
|
||||||
if (!configuration.build.command.python.module) configuration.build.command.python.module = 'main'
|
if (!configuration.build.command.python.module)
|
||||||
if (!configuration.build.command.python.instance) configuration.build.command.python.instance = 'app'
|
configuration.build.command.python.module = 'main';
|
||||||
|
if (!configuration.build.command.python.instance)
|
||||||
|
configuration.build.command.python.instance = 'app';
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration.build.container.baseSHA = crypto
|
configuration.build.container.baseSHA = crypto
|
||||||
@@ -77,7 +82,10 @@ export function setDefaultConfiguration(configuration) {
|
|||||||
|
|
||||||
export async function precheckDeployment(configuration) {
|
export async function precheckDeployment(configuration) {
|
||||||
const services = (await docker.engine.listServices()).filter(
|
const services = (await docker.engine.listServices()).filter(
|
||||||
(r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application' && JSON.parse(r.Spec.Labels.configuration).publish.domain === configuration.publish.domain
|
(r) =>
|
||||||
|
r.Spec.Labels.managedBy === 'coolify' &&
|
||||||
|
r.Spec.Labels.type === 'application' &&
|
||||||
|
JSON.parse(r.Spec.Labels.configuration).publish.domain === configuration.publish.domain
|
||||||
);
|
);
|
||||||
let foundService = false;
|
let foundService = false;
|
||||||
let configChanged = false;
|
let configChanged = false;
|
||||||
@@ -86,7 +94,6 @@ export async function precheckDeployment(configuration) {
|
|||||||
for (const service of services) {
|
for (const service of services) {
|
||||||
const running = JSON.parse(service.Spec.Labels.configuration);
|
const running = JSON.parse(service.Spec.Labels.configuration);
|
||||||
if (running) {
|
if (running) {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
running.repository.id === configuration.repository.id &&
|
running.repository.id === configuration.repository.id &&
|
||||||
running.repository.branch === configuration.repository.branch
|
running.repository.branch === configuration.repository.branch
|
||||||
@@ -117,26 +124,27 @@ export async function precheckDeployment(configuration) {
|
|||||||
|
|
||||||
const compareObjects = (a, b) => {
|
const compareObjects = (a, b) => {
|
||||||
if (a === b) return true;
|
if (a === b) return true;
|
||||||
|
|
||||||
if (typeof a != 'object' || typeof b != 'object' || a == null || b == null) return false;
|
if (typeof a != 'object' || typeof b != 'object' || a == null || b == null) return false;
|
||||||
|
|
||||||
let keysA = Object.keys(a), keysB = Object.keys(b);
|
const keysA = Object.keys(a),
|
||||||
|
keysB = Object.keys(b);
|
||||||
|
|
||||||
if (keysA.length != keysB.length) return false;
|
if (keysA.length != keysB.length) return false;
|
||||||
|
|
||||||
for (let key of keysA) {
|
for (const key of keysA) {
|
||||||
if (!keysB.includes(key)) return false;
|
if (!keysB.includes(key)) return false;
|
||||||
|
|
||||||
if (typeof a[key] === 'function' || typeof b[key] === 'function') {
|
if (typeof a[key] === 'function' || typeof b[key] === 'function') {
|
||||||
if (a[key].toString() != b[key].toString()) return false;
|
if (a[key].toString() != b[key].toString()) return false;
|
||||||
} else {
|
} else {
|
||||||
if (!compareObjects(a[key], b[key])) return false;
|
if (!compareObjects(a[key], b[key])) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
|
||||||
const runningWithoutContainer = JSON.parse(JSON.stringify(running));
|
const runningWithoutContainer = JSON.parse(JSON.stringify(running));
|
||||||
delete runningWithoutContainer.build.container;
|
delete runningWithoutContainer.build.container;
|
||||||
|
|
||||||
@@ -145,19 +153,23 @@ export async function precheckDeployment(configuration) {
|
|||||||
|
|
||||||
// If only the configuration changed
|
// If only the configuration changed
|
||||||
if (
|
if (
|
||||||
!compareObjects(runningWithoutContainer.build,configurationWithoutContainer.build) ||
|
!compareObjects(runningWithoutContainer.build, configurationWithoutContainer.build) ||
|
||||||
!compareObjects(runningWithoutContainer.publish,configurationWithoutContainer.publish) ||
|
!compareObjects(runningWithoutContainer.publish, configurationWithoutContainer.publish) ||
|
||||||
runningWithoutContainer.general.isPreviewDeploymentEnabled !==
|
runningWithoutContainer.general.isPreviewDeploymentEnabled !==
|
||||||
configurationWithoutContainer.general.isPreviewDeploymentEnabled
|
configurationWithoutContainer.general.isPreviewDeploymentEnabled
|
||||||
){
|
) {
|
||||||
configChanged = true;
|
configChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If only the image changed
|
// If only the image changed
|
||||||
if (running.build.container.tag !== configuration.build.container.tag) imageChanged = true;
|
if (running.build.container.tag !== configuration.build.container.tag) imageChanged = true;
|
||||||
// If build pack changed, forceUpdate the service
|
// If build pack changed, forceUpdate the service
|
||||||
if (running.build.pack !== configuration.build.pack) forceUpdate = true;
|
if (running.build.pack !== configuration.build.pack) forceUpdate = true;
|
||||||
if (configuration.general.isPreviewDeploymentEnabled && configuration.general.pullRequest !== 0) forceUpdate = true
|
if (
|
||||||
|
configuration.general.isPreviewDeploymentEnabled &&
|
||||||
|
configuration.general.pullRequest !== 0
|
||||||
|
)
|
||||||
|
forceUpdate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,5 +186,9 @@ export async function precheckDeployment(configuration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function updateServiceLabels(configuration) {
|
export async function updateServiceLabels(configuration) {
|
||||||
return await execShellAsync(`docker service update --label-add configuration='${JSON.stringify(configuration)}' ${configuration.build.container.name}_${configuration.build.container.name}`)
|
return await execShellAsync(
|
||||||
|
`docker service update --label-add configuration='${JSON.stringify(configuration)}' ${
|
||||||
|
configuration.build.container.name
|
||||||
|
}_${configuration.build.container.name}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,12 +42,8 @@ export default async function (configuration, imageChanged) {
|
|||||||
'`) && PathPrefix(`' +
|
'`) && PathPrefix(`' +
|
||||||
configuration.publish.path +
|
configuration.publish.path +
|
||||||
'`)',
|
'`)',
|
||||||
'traefik.http.routers.' +
|
'traefik.http.routers.' + containerName + '.tls.certresolver=letsencrypt',
|
||||||
containerName +
|
'traefik.http.routers.' + containerName + '.middlewares=global-compress'
|
||||||
'.tls.certresolver=letsencrypt',
|
|
||||||
'traefik.http.routers.' +
|
|
||||||
containerName +
|
|
||||||
'.middlewares=global-compress'
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +72,7 @@ export default async function (configuration, imageChanged) {
|
|||||||
await delay(10000);
|
await delay(10000);
|
||||||
await purgeImagesContainers(found, true);
|
await purgeImagesContainers(found, true);
|
||||||
}
|
}
|
||||||
purgeImagesAsync(configuration)
|
purgeImagesAsync(configuration);
|
||||||
|
|
||||||
await saveAppLog('### Published done!', configuration);
|
await saveAppLog('### Published done!', configuration);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import ApplicationLog from '$models/ApplicationLog';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { version } from '../../../../package.json';
|
import { version } from '../../../../package.json';
|
||||||
|
|
||||||
|
|
||||||
function generateTimestamp() {
|
function generateTimestamp() {
|
||||||
return `${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} `;
|
return `${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} `;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ const publishPython = (configuration) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function (configuration) {
|
export default async function (configuration) {
|
||||||
await fs.writeFile(
|
await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishPython(configuration));
|
||||||
`${configuration.general.workdir}/Dockerfile`,
|
|
||||||
publishPython(configuration)
|
|
||||||
);
|
|
||||||
const stream = await docker.engine.buildImage(
|
const stream = await docker.engine.buildImage(
|
||||||
{ src: ['.'], context: configuration.general.workdir },
|
{ src: ['.'], context: configuration.general.workdir },
|
||||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import Deployment from '$models/Deployment';
|
import Deployment from '$models/Deployment';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import buildContainer from './buildContainer';
|
import buildContainer from './buildContainer';
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export function execShellAsync(cmd, opts = {}) {
|
|||||||
shell.config.silent = true;
|
shell.config.silent = true;
|
||||||
shell.exec(cmd, opts, async function (code, stdout, stderr) {
|
shell.exec(cmd, opts, async function (code, stdout, stderr) {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
await saveServerLog({ message: JSON.stringify({ cmd, opts, code, stdout, stderr }) })
|
await saveServerLog({ message: JSON.stringify({ cmd, opts, code, stdout, stderr }) });
|
||||||
return reject(new Error(stderr));
|
return reject(new Error(stderr));
|
||||||
}
|
}
|
||||||
return resolve(stdout);
|
return resolve(stdout);
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export const publicPages = ['/', '/api/v1/login/github/app', '/api/v1/webhooks/deploy', '/success'];
|
export const publicPages = ['/', '/api/v1/login/github/app', '/api/v1/webhooks/deploy', '/success'];
|
||||||
export const VITE_GITHUB_APP_NAME = import.meta.env.VITE_GITHUB_APP_NAME
|
export const VITE_GITHUB_APP_NAME = import.meta.env.VITE_GITHUB_APP_NAME;
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ const ApplicationLogsSchema = new Schema({
|
|||||||
|
|
||||||
ApplicationLogsSchema.set('timestamps', true);
|
ApplicationLogsSchema.set('timestamps', true);
|
||||||
|
|
||||||
export default mongoose.models['logs-application'] || mongoose.model('logs-application', ApplicationLogsSchema);
|
export default mongoose.models['logs-application'] ||
|
||||||
|
mongoose.model('logs-application', ApplicationLogsSchema);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const ConfigurationSchema = new Schema({
|
|||||||
nickname: { type: String, required: true },
|
nickname: { type: String, required: true },
|
||||||
workdir: { type: String, required: true },
|
workdir: { type: String, required: true },
|
||||||
isPreviewDeploymentEnabled: { type: Boolean, required: true, default: false },
|
isPreviewDeploymentEnabled: { type: Boolean, required: true, default: false },
|
||||||
pullRequest: { type: Number, required: true, default: 0 },
|
pullRequest: { type: Number, required: true, default: 0 }
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
pack: { type: String, required: true },
|
pack: { type: String, required: true },
|
||||||
@@ -31,25 +31,25 @@ const ConfigurationSchema = new Schema({
|
|||||||
start: { type: String },
|
start: { type: String },
|
||||||
python: {
|
python: {
|
||||||
module: { type: String },
|
module: { type: String },
|
||||||
instance: { type: String },
|
instance: { type: String }
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
name: { type: String, required: true },
|
name: { type: String, required: true },
|
||||||
tag: { type: String, required: true },
|
tag: { type: String, required: true },
|
||||||
baseSHA: { type: String, required: true },
|
baseSHA: { type: String, required: true }
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
publish: {
|
publish: {
|
||||||
directory: { type: String },
|
directory: { type: String },
|
||||||
domain: { type: String, required: true },
|
domain: { type: String, required: true },
|
||||||
path: { type: String },
|
path: { type: String },
|
||||||
port: { type: Number },
|
port: { type: Number },
|
||||||
secrets: { type: Array },
|
secrets: { type: Array }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ConfigurationSchema.set('timestamps', true);
|
ConfigurationSchema.set('timestamps', true);
|
||||||
|
|
||||||
export default mongoose.models['configuration'] || mongoose.model('configuration', ConfigurationSchema);
|
export default mongoose.models['configuration'] ||
|
||||||
|
mongoose.model('configuration', ConfigurationSchema);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { version } from '../../package.json'
|
import { version } from '../../package.json';
|
||||||
const { Schema, Document } = mongoose;
|
const { Schema, Document } = mongoose;
|
||||||
|
|
||||||
// export interface ILogsServer extends Document {
|
// export interface ILogsServer extends Document {
|
||||||
|
|||||||
@@ -53,12 +53,13 @@
|
|||||||
let upgradeDisabled = false;
|
let upgradeDisabled = false;
|
||||||
let upgradeDone = false;
|
let upgradeDone = false;
|
||||||
let showAck = false;
|
let showAck = false;
|
||||||
|
let globalFeatureFlag = browser && localStorage.getItem('globalFeatureFlag')
|
||||||
const options = {
|
const options = {
|
||||||
duration: 2000
|
duration: 2000
|
||||||
};
|
};
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
upgradeAvailable = await checkUpgrade();
|
upgradeAvailable = await checkUpgrade();
|
||||||
browser && localStorage.removeItem('token')
|
browser && localStorage.removeItem('token');
|
||||||
if (!localStorage.getItem('automaticErrorReportsAck')) {
|
if (!localStorage.getItem('automaticErrorReportsAck')) {
|
||||||
showAck = true;
|
showAck = true;
|
||||||
if (latest?.coolify[branch]?.settings?.sendErrors) {
|
if (latest?.coolify[branch]?.settings?.sendErrors) {
|
||||||
@@ -224,6 +225,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div class="flex-1" />
|
<div class="flex-1" />
|
||||||
|
{#if globalFeatureFlag}
|
||||||
|
<Tooltip position="right" label="Server(s)">
|
||||||
|
<div
|
||||||
|
class="p-2 hover:bg-warmGray-700 rounded hover:text-red-500 mb-4 transition-all duration-100 cursor-pointer"
|
||||||
|
on:click={() => goto('/servers')}
|
||||||
|
class:text-red-500={$page.path === '/servers' || $page.path.startsWith('/servers')}
|
||||||
|
class:bg-warmGray-700={$page.path === '/servers' || $page.path.startsWith('/servers')}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="w-8"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
{/if}
|
||||||
<Tooltip position="right" label="Settings">
|
<Tooltip position="right" label="Settings">
|
||||||
<button
|
<button
|
||||||
class="p-2 hover:bg-warmGray-700 rounded hover:text-yellow-500 transition-all duration-100 cursor-pointer"
|
class="p-2 hover:bg-warmGray-700 rounded hover:text-yellow-500 transition-all duration-100 cursor-pointer"
|
||||||
@@ -287,7 +313,7 @@
|
|||||||
</main>
|
</main>
|
||||||
{#if upgradeAvailable && $page.path !== '/success' && $page.path !== '/'}
|
{#if upgradeAvailable && $page.path !== '/success' && $page.path !== '/'}
|
||||||
<footer
|
<footer
|
||||||
class="fixed bottom-0 right-0 p-4 px-6 w-auto rounded-tl text-white hover:scale-110 transform transition duration-100"
|
class="fixed bottom-0 right-0 p-4 px-6 w-auto rounded-tl text-white hover:scale-110 transition duration-100"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div />
|
<div />
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ export async function post(request: Request) {
|
|||||||
const { DOMAIN } = process.env;
|
const { DOMAIN } = process.env;
|
||||||
const configuration = setDefaultConfiguration(request.body);
|
const configuration = setDefaultConfiguration(request.body);
|
||||||
const configurationFound = await Configuration.find({
|
const configurationFound = await Configuration.find({
|
||||||
'repository.id': { '$ne': configuration.repository.id },
|
'repository.id': { $ne: configuration.repository.id },
|
||||||
'publish.domain': configuration.publish.domain
|
'publish.domain': configuration.publish.domain
|
||||||
}).select('-_id -__v -createdAt -updatedAt')
|
}).select('-_id -__v -createdAt -updatedAt');
|
||||||
if (configurationFound.length > 0 || configuration.publish.domain === DOMAIN) {
|
if (configurationFound.length > 0 || configuration.publish.domain === DOMAIN) {
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -24,7 +24,7 @@ export async function post(request: Request) {
|
|||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: { success: true, message: 'OK' }
|
body: { success: true, message: 'OK' }
|
||||||
}
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await saveServerLog(error);
|
await saveServerLog(error);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export async function post(request: Request) {
|
|||||||
'repository.name': name,
|
'repository.name': name,
|
||||||
'repository.organization': organization,
|
'repository.organization': organization,
|
||||||
'repository.branch': branch
|
'repository.branch': branch
|
||||||
}).select('-_id -__v -createdAt -updatedAt')
|
}).select('-_id -__v -createdAt -updatedAt');
|
||||||
|
|
||||||
if (configurationFound) {
|
if (configurationFound) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,26 +9,40 @@ import type { Request } from '@sveltejs/kit';
|
|||||||
export async function post(request: Request) {
|
export async function post(request: Request) {
|
||||||
const { name, organization, branch, isPreviewDeploymentEnabled }: any = request.body || {};
|
const { name, organization, branch, isPreviewDeploymentEnabled }: any = request.body || {};
|
||||||
if (name && organization && branch) {
|
if (name && organization && branch) {
|
||||||
const configuration = await Configuration.findOneAndUpdate({
|
const configuration = await Configuration.findOneAndUpdate(
|
||||||
'repository.name': name,
|
{
|
||||||
'repository.organization': organization,
|
'repository.name': name,
|
||||||
'repository.branch': branch
|
'repository.organization': organization,
|
||||||
}, { $set: { 'general.isPreviewDeploymentEnabled': isPreviewDeploymentEnabled, 'general.pullRequest': 0 } }, { new: true }).select('-_id -__v -createdAt -updatedAt')
|
'repository.branch': branch
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
'general.isPreviewDeploymentEnabled': isPreviewDeploymentEnabled,
|
||||||
|
'general.pullRequest': 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ new: true }
|
||||||
|
).select('-_id -__v -createdAt -updatedAt');
|
||||||
if (!isPreviewDeploymentEnabled) {
|
if (!isPreviewDeploymentEnabled) {
|
||||||
const found = await Configuration.find({
|
const found = await Configuration.find({
|
||||||
'repository.name': name,
|
'repository.name': name,
|
||||||
'repository.organization': organization,
|
'repository.organization': organization,
|
||||||
'repository.branch': branch,
|
'repository.branch': branch,
|
||||||
'general.pullRequest': { '$ne': 0 }
|
'general.pullRequest': { $ne: 0 }
|
||||||
})
|
});
|
||||||
for (const prDeployment of found) {
|
for (const prDeployment of found) {
|
||||||
await Configuration.findOneAndRemove({
|
await Configuration.findOneAndRemove({
|
||||||
'repository.name': name,
|
'repository.name': name,
|
||||||
'repository.organization': organization,
|
'repository.organization': organization,
|
||||||
'repository.branch': branch,
|
'repository.branch': branch,
|
||||||
'publish.domain': prDeployment.publish.domain
|
'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) {
|
for (const deploy of deploys) {
|
||||||
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
||||||
await Deployment.deleteMany({ deployId: deploy.deployId });
|
await Deployment.deleteMany({ deployId: deploy.deployId });
|
||||||
@@ -51,7 +65,6 @@ export async function post(request: Request) {
|
|||||||
success: true
|
success: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
status: 500,
|
status: 500,
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ export async function post(request: Request) {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await cloneRepository(configuration);
|
await cloneRepository(configuration);
|
||||||
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(configuration);
|
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(
|
||||||
|
configuration
|
||||||
|
);
|
||||||
if (foundService && !forceUpdate && !imageChanged && !configChanged) {
|
if (foundService && !forceUpdate && !imageChanged && !configChanged) {
|
||||||
cleanupTmp(configuration.general.workdir);
|
cleanupTmp(configuration.general.workdir);
|
||||||
return {
|
return {
|
||||||
@@ -60,15 +62,17 @@ export async function post(request: Request) {
|
|||||||
nickname
|
nickname
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
await Configuration.findOneAndUpdate({
|
await Configuration.findOneAndUpdate(
|
||||||
'repository.id': id,
|
{
|
||||||
'repository.organization': organization,
|
'repository.id': id,
|
||||||
'repository.name': name,
|
'repository.organization': organization,
|
||||||
'repository.branch': branch,
|
'repository.name': name,
|
||||||
'general.pullRequest': { '$in': [null, 0] },
|
'repository.branch': branch,
|
||||||
},
|
'general.pullRequest': { $in: [null, 0] }
|
||||||
|
},
|
||||||
{ ...configuration },
|
{ ...configuration },
|
||||||
{ upsert: true, new: true })
|
{ upsert: true, new: true }
|
||||||
|
);
|
||||||
|
|
||||||
queueAndBuild(configuration, imageChanged);
|
queueAndBuild(configuration, imageChanged);
|
||||||
return {
|
return {
|
||||||
@@ -81,7 +85,7 @@ export async function post(request: Request) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
await Deployment.findOneAndUpdate(
|
await Deployment.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
repoId: configuration.repository.id,
|
repoId: configuration.repository.id,
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ export async function get(request: Request) {
|
|||||||
.select('-_id -__v')
|
.select('-_id -__v')
|
||||||
.sort({ createdAt: 'asc' });
|
.sort({ createdAt: 'asc' });
|
||||||
|
|
||||||
const deploy: any = await Deployment.findOne({ deployId })
|
const deploy: any = await Deployment.findOne({ deployId }).select('-_id -__v');
|
||||||
.select('-_id -__v')
|
|
||||||
const finalLogs: any = {};
|
const finalLogs: any = {};
|
||||||
finalLogs.progress = deploy.progress;
|
finalLogs.progress = deploy.progress;
|
||||||
finalLogs.events = logs.map((log) => log.event);
|
finalLogs.events = logs.map((log) => log.event);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export async function get(request: Request) {
|
|||||||
const updatedAt = dayjs(d.updatedAt).utc();
|
const updatedAt = dayjs(d.updatedAt).utc();
|
||||||
finalLogs.took = updatedAt.diff(dayjs(d.createdAt)) / 1000;
|
finalLogs.took = updatedAt.diff(dayjs(d.createdAt)) / 1000;
|
||||||
finalLogs.since = updatedAt.fromNow();
|
finalLogs.since = updatedAt.fromNow();
|
||||||
finalLogs.isPr = d.domain.startsWith('pr')
|
finalLogs.isPr = d.domain.startsWith('pr');
|
||||||
return finalLogs;
|
return finalLogs;
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export async function get(request: Request) {
|
|||||||
body: { success: true, logs }
|
body: { success: true, logs }
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
await saveServerLog(error);
|
await saveServerLog(error);
|
||||||
return {
|
return {
|
||||||
status: 500,
|
status: 500,
|
||||||
|
|||||||
@@ -16,25 +16,25 @@ export async function post(request: Request) {
|
|||||||
'repository.name': name,
|
'repository.name': name,
|
||||||
'repository.branch': branch,
|
'repository.branch': branch,
|
||||||
'publish.domain': domain
|
'publish.domain': domain
|
||||||
})
|
});
|
||||||
if (configurationFound) {
|
if (configurationFound) {
|
||||||
const id = configurationFound._id
|
const id = configurationFound._id;
|
||||||
if (configurationFound?.general?.pullRequest === 0) {
|
if (configurationFound?.general?.pullRequest === 0) {
|
||||||
// Main deployment deletion request; deleting main + PRs
|
// Main deployment deletion request; deleting main + PRs
|
||||||
const allConfiguration = await Configuration.find({
|
const allConfiguration = await Configuration.find({
|
||||||
'repository.name': name,
|
'repository.name': name,
|
||||||
'repository.organization': organization,
|
'repository.organization': organization,
|
||||||
'repository.branch': branch,
|
'repository.branch': branch
|
||||||
})
|
});
|
||||||
for (const config of allConfiguration) {
|
for (const config of allConfiguration) {
|
||||||
await Configuration.findOneAndRemove({
|
await Configuration.findOneAndRemove({
|
||||||
'repository.name': config.repository.name,
|
'repository.name': config.repository.name,
|
||||||
'repository.organization': config.repository.organization,
|
'repository.organization': config.repository.organization,
|
||||||
'repository.branch': config.repository.branch,
|
'repository.branch': config.repository.branch
|
||||||
})
|
});
|
||||||
await execShellAsync(`docker stack rm ${config.build.container.name}`);
|
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) {
|
for (const deploy of deploys) {
|
||||||
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
||||||
await Deployment.deleteMany({ deployId: deploy.deployId });
|
await Deployment.deleteMany({ deployId: deploy.deployId });
|
||||||
@@ -43,9 +43,9 @@ export async function post(request: Request) {
|
|||||||
purgeImagesAsync(configurationFound);
|
purgeImagesAsync(configurationFound);
|
||||||
} else {
|
} else {
|
||||||
// Delete only PRs
|
// Delete only PRs
|
||||||
await Configuration.findByIdAndRemove(id)
|
await Configuration.findByIdAndRemove(id);
|
||||||
await execShellAsync(`docker stack rm ${configurationFound.build.container.name}`);
|
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) {
|
for (const deploy of deploys) {
|
||||||
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
||||||
await Deployment.deleteMany({ deployId: deploy.deployId });
|
await Deployment.deleteMany({ deployId: deploy.deployId });
|
||||||
@@ -63,7 +63,7 @@ export async function post(request: Request) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
return {
|
return {
|
||||||
status: 500,
|
status: 500,
|
||||||
error: {
|
error: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { docker } from '$lib/api/docker';
|
import { docker } from '$lib/api/docker';
|
||||||
import type { Request } from '@sveltejs/kit';
|
import type { Request } from '@sveltejs/kit';
|
||||||
import Configuration from '$models/Configuration'
|
import Configuration from '$models/Configuration';
|
||||||
export async function get(request: Request) {
|
export async function get(request: Request) {
|
||||||
// Should update this to get data from mongodb and update db with the currently running services on start!
|
// Should update this to get data from mongodb and update db with the currently running services on start!
|
||||||
const dockerServices = await docker.engine.listServices();
|
const dockerServices = await docker.engine.listServices();
|
||||||
@@ -34,21 +34,21 @@ export async function get(request: Request) {
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
const configurations = await Configuration.find({
|
const configurations = await Configuration.find({
|
||||||
'general.pullRequest': { '$in': [null, 0] }
|
'general.pullRequest': { $in: [null, 0] }
|
||||||
}).select('-_id -__v -createdAt')
|
}).select('-_id -__v -createdAt');
|
||||||
const applications = []
|
const applications = [];
|
||||||
for (const configuration of configurations) {
|
for (const configuration of configurations) {
|
||||||
const foundPRDeployments = await Configuration.find({
|
const foundPRDeployments = await Configuration.find({
|
||||||
'repository.id': configuration.repository.id,
|
'repository.id': configuration.repository.id,
|
||||||
'repository.branch': configuration.repository.branch,
|
'repository.branch': configuration.repository.branch,
|
||||||
'general.pullRequest': { '$ne': 0 }
|
'general.pullRequest': { $ne: 0 }
|
||||||
}).select('-_id -__v -createdAt')
|
}).select('-_id -__v -createdAt');
|
||||||
const payload = {
|
const payload = {
|
||||||
configuration,
|
configuration,
|
||||||
UpdatedAt: configuration.updatedAt,
|
UpdatedAt: configuration.updatedAt,
|
||||||
prBuilds: foundPRDeployments.length > 0 ? true : false,
|
prBuilds: foundPRDeployments.length > 0 ? true : false
|
||||||
}
|
};
|
||||||
applications.push(payload)
|
applications.push(payload);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
25
src/routes/api/v1/servers/cleanups/caches.ts
Normal file
25
src/routes/api/v1/servers/cleanups/caches.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { saveServerLog } from '$lib/api/applications/logging';
|
||||||
|
import { execShellAsync } from '$lib/api/common';
|
||||||
|
import type { Request } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
|
||||||
|
export async function post(request: Request) {
|
||||||
|
try {
|
||||||
|
const output = await execShellAsync('docker builder prune -af')
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
message: 'OK',
|
||||||
|
output: output.replace(/^(?=\n)$|^\s*|\s*$|\n\n+/gm,"").split('\n').pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
await saveServerLog(error);
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: {
|
||||||
|
error: error.message || error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/routes/api/v1/servers/cleanups/containers.ts
Normal file
25
src/routes/api/v1/servers/cleanups/containers.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { saveServerLog } from '$lib/api/applications/logging';
|
||||||
|
import { execShellAsync } from '$lib/api/common';
|
||||||
|
import type { Request } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
|
||||||
|
export async function post(request: Request) {
|
||||||
|
try {
|
||||||
|
const output = await execShellAsync('docker container prune -f')
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
message: 'OK',
|
||||||
|
output: output.replace(/^(?=\n)$|^\s*|\s*$|\n\n+/gm,"").split('\n').pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
await saveServerLog(error);
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: {
|
||||||
|
error: error.message || error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/routes/api/v1/servers/cleanups/images.ts
Normal file
25
src/routes/api/v1/servers/cleanups/images.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { saveServerLog } from '$lib/api/applications/logging';
|
||||||
|
import { execShellAsync } from '$lib/api/common';
|
||||||
|
import type { Request } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
|
||||||
|
export async function post(request: Request) {
|
||||||
|
try {
|
||||||
|
const output = await execShellAsync('docker image prune -af')
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
message: 'OK',
|
||||||
|
output: output.replace(/^(?=\n)$|^\s*|\s*$|\n\n+/gm,"").split('\n').pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
await saveServerLog(error);
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: {
|
||||||
|
error: error.message || error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/routes/api/v1/servers/cleanups/volumes.ts
Normal file
25
src/routes/api/v1/servers/cleanups/volumes.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { saveServerLog } from '$lib/api/applications/logging';
|
||||||
|
import { execShellAsync } from '$lib/api/common';
|
||||||
|
import type { Request } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
|
||||||
|
export async function post(request: Request) {
|
||||||
|
try {
|
||||||
|
const output = await execShellAsync('docker volume prune -f')
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
message: 'OK',
|
||||||
|
output: output.replace(/^(?=\n)$|^\s*|\s*$|\n\n+/gm,"").split('\n').pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
await saveServerLog(error);
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: {
|
||||||
|
error: error.message || error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/routes/api/v1/servers/index.ts
Normal file
34
src/routes/api/v1/servers/index.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { saveServerLog } from '$lib/api/applications/logging';
|
||||||
|
import { execShellAsync } from '$lib/api/common';
|
||||||
|
import { docker } from '$lib/api/docker';
|
||||||
|
import type { Request } from '@sveltejs/kit';
|
||||||
|
import systeminformation from 'systeminformation'
|
||||||
|
|
||||||
|
export async function get(request: Request) {
|
||||||
|
try {
|
||||||
|
const df = await execShellAsync(
|
||||||
|
`docker system df --format '{{ json . }}'`
|
||||||
|
);
|
||||||
|
const dockerReclaimable = df
|
||||||
|
.split('\n')
|
||||||
|
.filter((n) => n)
|
||||||
|
.map((s) => JSON.parse(s))
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
hostname: await (await systeminformation.osInfo()).hostname,
|
||||||
|
filesystems: await (await systeminformation.fsSize()).filter(fs => !fs.fs.match('/dev/loop') || !fs.fs.match('/var/lib/docker/')),
|
||||||
|
dockerReclaimable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
await saveServerLog(error);
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: {
|
||||||
|
error: error.message || error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import type { Request } from '@sveltejs/kit';
|
|||||||
|
|
||||||
export async function get(request: Request) {
|
export async function get(request: Request) {
|
||||||
const { serviceName } = request.params;
|
const { serviceName } = request.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const service = (await docker.engine.listServices()).find(
|
const service = (await docker.engine.listServices()).find(
|
||||||
(r) =>
|
(r) =>
|
||||||
|
|||||||
74
src/routes/api/v1/services/deploy/code-server/index.ts
Normal file
74
src/routes/api/v1/services/deploy/code-server/index.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
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/code-server';
|
||||||
|
const deployId = 'code-server';
|
||||||
|
// const environment = [
|
||||||
|
// { name: 'DOCKER_USER', value: 'root' }
|
||||||
|
|
||||||
|
// ];
|
||||||
|
// const generateEnvsCodeServer = {};
|
||||||
|
// for (const env of environment) generateEnvsCodeServer[env.name] = env.value;
|
||||||
|
|
||||||
|
const stack = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[deployId]: {
|
||||||
|
image: 'codercom/code-server',
|
||||||
|
command: 'code-server --disable-telemetry',
|
||||||
|
networks: [`${docker.network}`],
|
||||||
|
volumes: [`${deployId}-code-server-data:/home/coder`],
|
||||||
|
// environment: generateEnvsCodeServer,
|
||||||
|
deploy: {
|
||||||
|
...baseServiceConfiguration,
|
||||||
|
labels: [
|
||||||
|
'managedBy=coolify',
|
||||||
|
'type=service',
|
||||||
|
'serviceName=code-server',
|
||||||
|
'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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: {
|
||||||
|
[`${deployId}-code-server-data`]: {
|
||||||
|
external: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await execShellAsync(`mkdir -p ${workdir}`);
|
||||||
|
await fs.writeFile(`${workdir}/stack.yml`, yaml.dump(stack));
|
||||||
|
await execShellAsync('docker stack rm code-server');
|
||||||
|
await execShellAsync(`cat ${workdir}/stack.yml | docker stack deploy --prune -c - ${deployId}`);
|
||||||
|
cleanupTmp(workdir);
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: { message: 'OK' }
|
||||||
|
};
|
||||||
|
}
|
||||||
25
src/routes/api/v1/services/deploy/code-server/password.ts
Normal file
25
src/routes/api/v1/services/deploy/code-server/password.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { execShellAsync } from '$lib/api/common';
|
||||||
|
import type { Request } from '@sveltejs/kit';
|
||||||
|
import yaml from "js-yaml"
|
||||||
|
|
||||||
|
export async function get(request: Request) {
|
||||||
|
// const { POSTGRESQL_USERNAME, POSTGRESQL_PASSWORD, POSTGRESQL_DATABASE } = JSON.parse(
|
||||||
|
// JSON.parse(
|
||||||
|
// await execShellAsync(
|
||||||
|
// "docker service inspect code-server_code-server --format='{{json .Spec.Labels.configuration}}'"
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
// ).generateEnvsPostgres;
|
||||||
|
const containers = (await execShellAsync("docker ps -a --format='{{json .Names}}'"))
|
||||||
|
.replace(/"/g, '')
|
||||||
|
.trim()
|
||||||
|
.split('\n');
|
||||||
|
const codeServer = containers.find((container) => container.startsWith('code-server'));
|
||||||
|
const configYaml = yaml.load(await execShellAsync(
|
||||||
|
`docker exec ${codeServer} cat /home/coder/.config/code-server/config.yaml`
|
||||||
|
))
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: { message: 'OK', password: configYaml.password }
|
||||||
|
};
|
||||||
|
}
|
||||||
77
src/routes/api/v1/services/deploy/minio/index.ts
Normal file
77
src/routes/api/v1/services/deploy/minio/index.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import type { Request } from '@sveltejs/kit';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
import generator from 'generate-password';
|
||||||
|
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/minio';
|
||||||
|
const deployId = 'minio';
|
||||||
|
const secrets = [
|
||||||
|
{ name: 'MINIO_ROOT_USER', value: generator.generate({ length: 12, numbers: true, strict: true }) },
|
||||||
|
{ name: 'MINIO_ROOT_PASSWORD', value: generator.generate({ length: 24, numbers: true, strict: true }) }
|
||||||
|
|
||||||
|
];
|
||||||
|
const generateEnvsMinIO = {};
|
||||||
|
for (const secret of secrets) generateEnvsMinIO[secret.name] = secret.value;
|
||||||
|
|
||||||
|
const stack = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[deployId]: {
|
||||||
|
image: 'minio/minio',
|
||||||
|
command: 'server /data',
|
||||||
|
networks: [`${docker.network}`],
|
||||||
|
environment: generateEnvsMinIO,
|
||||||
|
volumes: [`${deployId}-minio-data:/data`],
|
||||||
|
deploy: {
|
||||||
|
...baseServiceConfiguration,
|
||||||
|
labels: [
|
||||||
|
'managedBy=coolify',
|
||||||
|
'type=service',
|
||||||
|
'serviceName=minio',
|
||||||
|
'configuration=' +
|
||||||
|
JSON.stringify({
|
||||||
|
baseURL,
|
||||||
|
generateEnvsMinIO
|
||||||
|
}),
|
||||||
|
'traefik.enable=true',
|
||||||
|
'traefik.http.services.' + deployId + '.loadbalancer.server.port=9000',
|
||||||
|
'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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: {
|
||||||
|
[`${deployId}-minio-data`]: {
|
||||||
|
external: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await execShellAsync(`mkdir -p ${workdir}`);
|
||||||
|
await fs.writeFile(`${workdir}/stack.yml`, yaml.dump(stack));
|
||||||
|
await execShellAsync('docker stack rm minio');
|
||||||
|
await execShellAsync(`cat ${workdir}/stack.yml | docker stack deploy --prune -c - ${deployId}`);
|
||||||
|
cleanupTmp(workdir);
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: { message: 'OK' }
|
||||||
|
};
|
||||||
|
}
|
||||||
59
src/routes/api/v1/services/deploy/nocodb/index.ts
Normal file
59
src/routes/api/v1/services/deploy/nocodb/index.ts
Normal 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' }
|
||||||
|
};
|
||||||
|
}
|
||||||
173
src/routes/api/v1/services/deploy/wordpress/index.ts
Normal file
173
src/routes/api/v1/services/deploy/wordpress/index.ts
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
import type { Request } from '@sveltejs/kit';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
import generator from 'generate-password';
|
||||||
|
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, remoteDB, database, wordpressExtraConfiguration } = request.body;
|
||||||
|
const traefikURL = baseURL;
|
||||||
|
baseURL = `https://${baseURL}`;
|
||||||
|
console.log({ baseURL, remoteDB, database, wordpressExtraConfiguration })
|
||||||
|
|
||||||
|
const workdir = '/tmp/wordpress';
|
||||||
|
const deployId = `wp-${generator.generate({ length: 5, numbers: true, strict: true })}`
|
||||||
|
const defaultDatabaseName = generator.generate({ length: 12, numbers: true, strict: true })
|
||||||
|
const defaultDatabaseHost = `${deployId}-mysql`
|
||||||
|
const defaultDatabaseUser = generator.generate({ length: 12, numbers: true, strict: true })
|
||||||
|
const defaultDatabasePassword = generator.generate({ length: 24, numbers: true, strict: true })
|
||||||
|
const defaultDatabaseRootPassword = generator.generate({ length: 24, numbers: true, strict: true })
|
||||||
|
const defaultDatabaseRootUser = generator.generate({ length: 12, numbers: true, strict: true })
|
||||||
|
let secrets = [
|
||||||
|
{ name: 'WORDPRESS_DB_HOST', value: defaultDatabaseHost },
|
||||||
|
{ name: 'WORDPRESS_DB_USER', value: defaultDatabaseUser },
|
||||||
|
{ name: 'WORDPRESS_DB_PASSWORD', value: defaultDatabasePassword },
|
||||||
|
{ name: 'WORDPRESS_DB_NAME', value: defaultDatabaseName },
|
||||||
|
{ name: 'WORDPRESS_CONFIG_EXTRA', value: wordpressExtraConfiguration }
|
||||||
|
];
|
||||||
|
|
||||||
|
const generateEnvsMySQL = {
|
||||||
|
MYSQL_ROOT_PASSWORD: defaultDatabaseRootPassword,
|
||||||
|
MYSQL_ROOT_USER: defaultDatabaseRootUser,
|
||||||
|
MYSQL_USER: defaultDatabaseUser,
|
||||||
|
MYSQL_PASSWORD: defaultDatabasePassword,
|
||||||
|
MYSQL_DATABASE: defaultDatabaseName
|
||||||
|
};
|
||||||
|
const image = 'bitnami/mysql:8.0';
|
||||||
|
const volume = `${deployId}-mysql-data:/bitnami/mysql/data`;
|
||||||
|
|
||||||
|
if (remoteDB) {
|
||||||
|
secrets = [
|
||||||
|
{ name: 'WORDPRESS_DB_HOST', value: database.host },
|
||||||
|
{ name: 'WORDPRESS_DB_USER', value: database.user },
|
||||||
|
{ name: 'WORDPRESS_DB_PASSWORD', value: database.password },
|
||||||
|
{ name: 'WORDPRESS_DB_NAME', value: database.name },
|
||||||
|
{ name: 'WORDPRESS_TABLE_PREFIX', value: database.tablePrefix },
|
||||||
|
{ name: 'WORDPRESS_CONFIG_EXTRA', value: wordpressExtraConfiguration }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateEnvsWordpress = {};
|
||||||
|
for (const secret of secrets) generateEnvsWordpress[secret.name] = secret.value;
|
||||||
|
let stack = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[deployId]: {
|
||||||
|
image: 'wordpress',
|
||||||
|
networks: [`${docker.network}`],
|
||||||
|
environment: generateEnvsWordpress,
|
||||||
|
volumes: [`${deployId}-wordpress-data:/var/www/html`],
|
||||||
|
deploy: {
|
||||||
|
...baseServiceConfiguration,
|
||||||
|
labels: [
|
||||||
|
'managedBy=coolify',
|
||||||
|
'type=service',
|
||||||
|
'serviceName=' + deployId,
|
||||||
|
'configuration=' +
|
||||||
|
JSON.stringify({
|
||||||
|
deployId,
|
||||||
|
baseURL,
|
||||||
|
generateEnvsWordpress
|
||||||
|
}),
|
||||||
|
'traefik.enable=true',
|
||||||
|
'traefik.http.services.' + deployId + '.loadbalancer.server.port=80',
|
||||||
|
'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'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[`${deployId}-mysql`]: {
|
||||||
|
image,
|
||||||
|
networks: [`${docker.network}`],
|
||||||
|
environment: generateEnvsMySQL,
|
||||||
|
volumes: [volume],
|
||||||
|
deploy: {
|
||||||
|
...baseServiceConfiguration,
|
||||||
|
labels: [
|
||||||
|
'managedBy=coolify',
|
||||||
|
'type=service',
|
||||||
|
'serviceName=' + deployId,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
networks: {
|
||||||
|
[`${docker.network}`]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: {
|
||||||
|
[`${deployId}-wordpress-data`]: {
|
||||||
|
external: true
|
||||||
|
},
|
||||||
|
[`${deployId}-mysql-data`]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (remoteDB) {
|
||||||
|
stack = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[deployId]: {
|
||||||
|
image: 'wordpress',
|
||||||
|
networks: [`${docker.network}`],
|
||||||
|
environment: generateEnvsWordpress,
|
||||||
|
volumes: [`${deployId}-wordpress-data:/var/www/html`],
|
||||||
|
deploy: {
|
||||||
|
...baseServiceConfiguration,
|
||||||
|
labels: [
|
||||||
|
'managedBy=coolify',
|
||||||
|
'type=service',
|
||||||
|
'serviceName=' + deployId,
|
||||||
|
'configuration=' +
|
||||||
|
JSON.stringify({
|
||||||
|
deployId,
|
||||||
|
baseURL,
|
||||||
|
generateEnvsWordpress
|
||||||
|
}),
|
||||||
|
'traefik.enable=true',
|
||||||
|
'traefik.http.services.' + deployId + '.loadbalancer.server.port=80',
|
||||||
|
'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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: {
|
||||||
|
[`${deployId}-wordpress-data`]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
console.log(stack)
|
||||||
|
await execShellAsync(`mkdir -p ${workdir}`);
|
||||||
|
await fs.writeFile(`${workdir}/stack.yml`, yaml.dump(stack));
|
||||||
|
await execShellAsync(`docker stack rm ${deployId}`);
|
||||||
|
await execShellAsync(`cat ${workdir}/stack.yml | docker stack deploy --prune -c - ${deployId}`);
|
||||||
|
cleanupTmp(workdir);
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: { message: 'OK' }
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,9 +11,9 @@ import ApplicationLog from '$models/ApplicationLog';
|
|||||||
import { cleanupStuckedDeploymentsInDB } from '$lib/api/applications/cleanup';
|
import { cleanupStuckedDeploymentsInDB } from '$lib/api/applications/cleanup';
|
||||||
export async function post(request: Request) {
|
export async function post(request: Request) {
|
||||||
let configuration;
|
let configuration;
|
||||||
const allowedGithubEvents = ['push', 'pull_request']
|
const allowedGithubEvents = ['push', 'pull_request'];
|
||||||
const allowedPRActions = ['opened', , 'reopened', 'synchronize', 'closed']
|
const allowedPRActions = ['opened', 'reopened', 'synchronize', 'closed'];
|
||||||
const githubEvent = request.headers['x-github-event']
|
const githubEvent = request.headers['x-github-event'];
|
||||||
const { GITHUP_APP_WEBHOOK_SECRET } = process.env;
|
const { GITHUP_APP_WEBHOOK_SECRET } = process.env;
|
||||||
const hmac = crypto.createHmac('sha256', GITHUP_APP_WEBHOOK_SECRET);
|
const hmac = crypto.createHmac('sha256', GITHUP_APP_WEBHOOK_SECRET);
|
||||||
const digest = Buffer.from(
|
const digest = Buffer.from(
|
||||||
@@ -41,8 +41,8 @@ export async function post(request: Request) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const applications = await Configuration.find({
|
const applications = await Configuration.find({
|
||||||
'repository.id': request.body.repository.id,
|
'repository.id': request.body.repository.id
|
||||||
}).select('-_id -__v -createdAt -updatedAt')
|
}).select('-_id -__v -createdAt -updatedAt');
|
||||||
if (githubEvent === 'push') {
|
if (githubEvent === 'push') {
|
||||||
configuration = applications.find((r) => {
|
configuration = applications.find((r) => {
|
||||||
if (request.body.ref.startsWith('refs')) {
|
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) {
|
||||||
if (!configuration.general.isPreviewDeploymentEnabled) {
|
if (!configuration.general.isPreviewDeploymentEnabled) {
|
||||||
return {
|
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) {
|
if (!configuration) {
|
||||||
@@ -99,7 +101,7 @@ export async function post(request: Request) {
|
|||||||
'repository.name': name,
|
'repository.name': name,
|
||||||
'repository.branch': branch,
|
'repository.branch': branch,
|
||||||
'general.pullRequest': pullRequest
|
'general.pullRequest': pullRequest
|
||||||
})
|
});
|
||||||
await execShellAsync(`docker stack rm ${configuration.build.container.name}`);
|
await execShellAsync(`docker stack rm ${configuration.build.container.name}`);
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -110,7 +112,9 @@ export async function post(request: Request) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
await cloneRepository(configuration);
|
await cloneRepository(configuration);
|
||||||
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(configuration);
|
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(
|
||||||
|
configuration
|
||||||
|
);
|
||||||
if (foundService && !forceUpdate && !imageChanged && !configChanged) {
|
if (foundService && !forceUpdate && !imageChanged && !configChanged) {
|
||||||
cleanupTmp(configuration.general.workdir);
|
cleanupTmp(configuration.general.workdir);
|
||||||
return {
|
return {
|
||||||
@@ -149,27 +153,30 @@ export async function post(request: Request) {
|
|||||||
nickname
|
nickname
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
|
|
||||||
if (githubEvent === 'pull_request') {
|
if (githubEvent === 'pull_request') {
|
||||||
await Configuration.findOneAndUpdate({
|
await Configuration.findOneAndUpdate(
|
||||||
'repository.id': id,
|
{
|
||||||
'repository.organization': organization,
|
'repository.id': id,
|
||||||
'repository.name': name,
|
'repository.organization': organization,
|
||||||
'repository.branch': branch,
|
'repository.name': name,
|
||||||
'general.pullRequest': pullRequest
|
'repository.branch': branch,
|
||||||
},
|
'general.pullRequest': pullRequest
|
||||||
|
},
|
||||||
{ ...configuration },
|
{ ...configuration },
|
||||||
{ upsert: true, new: true })
|
{ upsert: true, new: true }
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await Configuration.findOneAndUpdate({
|
await Configuration.findOneAndUpdate(
|
||||||
'repository.id': id,
|
{
|
||||||
'repository.organization': organization,
|
'repository.id': id,
|
||||||
'repository.name': name,
|
'repository.organization': organization,
|
||||||
'repository.branch': branch,
|
'repository.name': name,
|
||||||
'general.pullRequest': { '$in': [null, 0] }
|
'repository.branch': branch,
|
||||||
},
|
'general.pullRequest': { $in: [null, 0] }
|
||||||
|
},
|
||||||
{ ...configuration },
|
{ ...configuration },
|
||||||
{ upsert: true, new: true })
|
{ upsert: true, new: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
queueAndBuild(configuration, imageChanged);
|
queueAndBuild(configuration, imageChanged);
|
||||||
@@ -183,7 +190,7 @@ export async function post(request: Request) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
// console.log(configuration)
|
// console.log(configuration)
|
||||||
if (configuration) {
|
if (configuration) {
|
||||||
cleanupTmp(configuration.general.workdir);
|
cleanupTmp(configuration.general.workdir);
|
||||||
@@ -216,7 +223,7 @@ export async function post(request: Request) {
|
|||||||
try {
|
try {
|
||||||
await cleanupStuckedDeploymentsInDB();
|
await cleanupStuckedDeploymentsInDB();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
{#each $dashboard.applications.deployed as application}
|
{#each $dashboard.applications.deployed as application}
|
||||||
<div class="px-4 pb-4">
|
<div class="px-4 pb-4">
|
||||||
<div
|
<div
|
||||||
class="relative rounded-xl p-6 bg-warmGray-800 border-2 border-dashed border-transparent hover:border-green-500 text-white shadow-md cursor-pointer ease-in-out transform hover:scale-105 duration-100 group"
|
class="relative rounded-xl p-6 bg-warmGray-800 border-2 border-dashed border-transparent hover:border-green-500 text-white shadow-md cursor-pointer ease-in-out hover:scale-105 duration-100 group"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
goto(
|
goto(
|
||||||
`/application/${application.configuration.repository.organization}/${application.configuration.repository.name}/${application.configuration.repository.branch}/configuration`
|
`/application/${application.configuration.repository.organization}/${application.configuration.repository.name}/${application.configuration.repository.branch}/configuration`
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
goto(`/database/${database.configuration.general.deployId}/configuration`)}
|
goto(`/database/${database.configuration.general.deployId}/configuration`)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="relative rounded-xl p-6 bg-warmGray-800 border-2 border-dashed border-transparent hover:border-purple-500 text-white shadow-md cursor-pointer ease-in-out transform hover:scale-105 duration-100 group"
|
class="relative rounded-xl p-6 bg-warmGray-800 border-2 border-dashed border-transparent hover:border-purple-500 text-white shadow-md cursor-pointer ease-in-out hover:scale-105 duration-100 group"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
{#if database.configuration.general.type == 'mongodb'}
|
{#if database.configuration.general.type == 'mongodb'}
|
||||||
|
|||||||
@@ -2,6 +2,14 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { dashboard } from '$store';
|
import { dashboard } from '$store';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
async function openConfiguration(service) {
|
||||||
|
if (service.serviceName === 'wordpress') {
|
||||||
|
goto(`/service/${service.configuration.deployId}/configuration`);
|
||||||
|
} else {
|
||||||
|
goto(`/service/${service.serviceName}/configuration`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -34,10 +42,10 @@
|
|||||||
<div
|
<div
|
||||||
in:fade={{ duration: 200 }}
|
in:fade={{ duration: 200 }}
|
||||||
class="px-4 pb-4"
|
class="px-4 pb-4"
|
||||||
on:click={() => goto(`/service/${service.serviceName}/configuration`)}
|
on:click={() => openConfiguration(service)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="relative rounded-xl p-6 bg-warmGray-800 border-2 border-dashed border-transparent hover:border-blue-500 text-white shadow-md cursor-pointer ease-in-out transform hover:scale-105 duration-100 group"
|
class="relative rounded-xl p-6 bg-warmGray-800 border-2 border-dashed border-transparent hover:border-blue-500 text-white shadow-md cursor-pointer ease-in-out hover:scale-105 duration-100 group"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
{#if service.serviceName == 'plausible'}
|
{#if service.serviceName == 'plausible'}
|
||||||
@@ -49,6 +57,60 @@
|
|||||||
/>
|
/>
|
||||||
<div class="text-white font-bold">Plausible Analytics</div>
|
<div class="text-white font-bold">Plausible Analytics</div>
|
||||||
</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>
|
||||||
|
{:else if service.serviceName == 'code-server'}
|
||||||
|
<div>
|
||||||
|
<svg class="w-10 absolute top-0 left-0 -m-6" viewBox="0 0 128 128">
|
||||||
|
<path
|
||||||
|
d="M3.656 45.043s-3.027-2.191.61-5.113l8.468-7.594s2.426-2.559 4.989-.328l78.175 59.328v28.45s-.039 4.468-5.757 3.976zm0 0"
|
||||||
|
fill="#2489ca"
|
||||||
|
/><path
|
||||||
|
d="M23.809 63.379L3.656 81.742s-2.07 1.543 0 4.305l9.356 8.527s2.222 2.395 5.508-.328l21.359-16.238zm0 0"
|
||||||
|
fill="#1070b3"
|
||||||
|
/><path
|
||||||
|
d="M59.184 63.531l36.953-28.285-.239-28.297S94.32.773 89.055 3.99L39.879 48.851zm0 0"
|
||||||
|
fill="#0877b9"
|
||||||
|
/><path
|
||||||
|
d="M90.14 123.797c2.145 2.203 4.747 1.48 4.747 1.48l28.797-14.222c3.687-2.52 3.171-5.645 3.171-5.645V20.465c0-3.735-3.812-5.024-3.812-5.024L98.082 3.38c-5.453-3.379-9.027.61-9.027.61s4.593-3.317 6.843 2.96v112.317c0 .773-.164 1.53-.492 2.214-.656 1.332-2.086 2.57-5.504 2.051zm0 0"
|
||||||
|
fill="#3c99d4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<div class="text-white font-bold">VSCode Server</div>
|
||||||
|
</div>
|
||||||
|
{:else if service.serviceName == 'minio'}
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
alt="minio"
|
||||||
|
class="w-7 absolute top-0 left-0 -my-7 -mx-3"
|
||||||
|
src="https://cdn.coollabs.io/assets/coolify/services/minio/MINIO_Bird.png"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="text-white font-bold">MinIO</div>
|
||||||
|
</div>
|
||||||
|
{:else if service.serviceName.match(/wp-/)}
|
||||||
|
<svg class="w-10 absolute top-0 left-0 -m-6" viewBox="0 0 128 128">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="white"
|
||||||
|
d="M64.094 126.224c34.275-.052 62.021-27.933 62.021-62.325 0-33.833-27.618-61.697-60.613-62.286C30.85.995 1.894 29.113 1.885 63.21c-.01 35.079 27.612 63.064 62.209 63.014zM63.993 4.63c32.907-.011 59.126 26.725 59.116 60.28-.011 31.679-26.925 58.18-59.092 58.187-32.771.007-59.125-26.563-59.124-59.608.002-32.193 26.766-58.848 59.1-58.859zM39.157 35.896c.538 1.793-.968 2.417-2.569 2.542-1.685.13-3.369.257-5.325.406 6.456 19.234 12.815 38.183 19.325 57.573.464-.759.655-.973.739-1.223 3.574-10.682 7.168-21.357 10.651-32.069.318-.977.16-2.271-.188-3.275-1.843-5.32-4.051-10.524-5.667-15.908-1.105-3.686-2.571-6.071-6.928-5.644-.742.073-1.648-1.524-2.479-2.349 1.005-.6 2.003-1.704 3.017-1.719a849.593 849.593 0 0126.618.008c1.018.017 2.016 1.15 3.021 1.765-.88.804-1.639 2.01-2.668 2.321-1.651.498-3.482.404-5.458.58l19.349 57.56c2.931-9.736 5.658-18.676 8.31-27.639 2.366-8.001.956-15.473-3.322-22.52-1.286-2.119-2.866-4.175-3.595-6.486-.828-2.629-1.516-5.622-1.077-8.259.745-4.469 4.174-6.688 8.814-7.113C74.333.881 34.431 9.317 19.728 34.922c5.66-.261 11.064-.604 16.472-.678 1.022-.013 2.717.851 2.957 1.652zm10.117 77.971c-.118.345-.125.729-.218 1.302 10.943 3.034 21.675 2.815 32.659-.886l-16.78-45.96c-5.37 15.611-10.52 30.575-15.661 45.544zm-8.456-2.078l-25.281-69.35c-11.405 22.278-2.729 56.268 25.281 69.35zm76.428-44.562c.802-10.534-2.832-25.119-5.97-27.125-.35 3.875-.106 8.186-1.218 12.114-2.617 9.255-5.817 18.349-8.899 27.468-3.35 9.912-6.832 19.779-10.257 29.666 16.092-9.539 24.935-23.618 26.344-42.123z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div class="text-white font-bold text-center">
|
||||||
|
Wordpress<span
|
||||||
|
class="flex text-xs items-center justify-center text-warmGray-300 group-hover:text-white"
|
||||||
|
>({service.configuration.baseURL.replace('https://', '')})</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
101
src/routes/servers/index.svelte
Normal file
101
src/routes/servers/index.svelte
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<script context="module">
|
||||||
|
/**
|
||||||
|
* @type {import('@sveltejs/kit').Load}
|
||||||
|
*/
|
||||||
|
export async function load({ fetch }) {
|
||||||
|
try {
|
||||||
|
const { hostname, filesystems,dockerReclaimable } = await (await fetch(`/api/v1/servers`)).json();
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
hostname,
|
||||||
|
filesystems,
|
||||||
|
dockerReclaimable
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
hostname: null,
|
||||||
|
filesystems: null,
|
||||||
|
dockerReclaimable: null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export let hostname;
|
||||||
|
export let filesystems;
|
||||||
|
export let dockerReclaimable;
|
||||||
|
import { browser } from '$app/env';
|
||||||
|
import { session } from '$app/stores';
|
||||||
|
import { request } from '$lib/request';
|
||||||
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
async function refetch() {
|
||||||
|
const data = await request('/api/v1/servers', $session)
|
||||||
|
filesystems = data.filesystems
|
||||||
|
dockerReclaimable = data.dockerReclaimable
|
||||||
|
}
|
||||||
|
async function cleanupVolumes() {
|
||||||
|
const { output } = await request('/api/v1/servers/cleanups/volumes', $session, {
|
||||||
|
body: {}
|
||||||
|
});
|
||||||
|
browser && toast.push(output);
|
||||||
|
await refetch()
|
||||||
|
}
|
||||||
|
async function cleanupImages() {
|
||||||
|
const { output } = await request('/api/v1/servers/cleanups/images', $session, {
|
||||||
|
body: {}
|
||||||
|
});
|
||||||
|
browser && toast.push(output);
|
||||||
|
await refetch()
|
||||||
|
}
|
||||||
|
async function cleanupBuildCache() {
|
||||||
|
const { output } = await request('/api/v1/servers/cleanups/caches', $session, {
|
||||||
|
body: {}
|
||||||
|
});
|
||||||
|
browser && toast.push(output);
|
||||||
|
await refetch()
|
||||||
|
}
|
||||||
|
async function cleanupContainers() {
|
||||||
|
const { output } = await request('/api/v1/servers/cleanups/containers', $session, {
|
||||||
|
body: {}
|
||||||
|
});
|
||||||
|
browser && toast.push(output);
|
||||||
|
await refetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="min-h-full text-white" in:fade={{ duration: 100 }}>
|
||||||
|
<div class="py-5 text-left px-6 text-3xl tracking-tight font-bold flex items-center">
|
||||||
|
<div>Servers</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div in:fade={{ duration: 100 }}>
|
||||||
|
<div class="max-w-4xl mx-auto px-6 pb-4 h-64 ">
|
||||||
|
<div class="text-center font-bold text-xl">{hostname}</div>
|
||||||
|
<div class="font-bold">Filesystem Usage</div>
|
||||||
|
{#each filesystems as filesystem}
|
||||||
|
<!-- <div>{JSON.stringify(filesystem)}</div> -->
|
||||||
|
<div class="text-xs">
|
||||||
|
{filesystem.mount}: {(filesystem.available / 1024 / 1024).toFixed()}MB ({filesystem.use}%) free of {(filesystem.size /1024 /1024).toFixed()}MB
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
<div class="font-bold">Docker Reclaimable</div>
|
||||||
|
{#each dockerReclaimable as reclaimable}
|
||||||
|
<div class="text-xs">
|
||||||
|
{reclaimable.Type}: {reclaimable.Reclaimable} of {reclaimable.Size}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<button class="button hover:bg-warmGray-700 bg-warmGray-800 rounded p-2 font-bold" on:click={cleanupVolumes}>Cleanup unused volumes</button>
|
||||||
|
<button class="button hover:bg-warmGray-700 bg-warmGray-800 rounded p-2 font-bold" on:click={cleanupImages}>Cleanup unused images</button>
|
||||||
|
<button class="button hover:bg-warmGray-700 bg-warmGray-800 rounded p-2 font-bold" on:click={cleanupBuildCache}>Cleanup build caches</button>
|
||||||
|
<button class="button hover:bg-warmGray-700 bg-warmGray-800 rounded p-2 font-bold" on:click={cleanupContainers}>Cleanup containers</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -8,29 +8,23 @@
|
|||||||
import Loading from '$components/Loading.svelte';
|
import Loading from '$components/Loading.svelte';
|
||||||
import Plausible from '$components/Service/Plausible.svelte';
|
import Plausible from '$components/Service/Plausible.svelte';
|
||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
|
import CodeServer from '$components/Service/CodeServer.svelte';
|
||||||
|
import MinIo from '$components/Service/MinIO.svelte';
|
||||||
let service = {};
|
let service = {};
|
||||||
async function loadServiceConfig() {
|
async function loadServiceConfig() {
|
||||||
if ($page.params.name) {
|
if ($page.params.name) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
service = await request(`/api/v1/services/${$page.params.name}`, $session);
|
service = await request(`/api/v1/services/${$page.params.name}`, $session);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
browser && toast.push(`Cannot find service ${$page.params.name}?!`);
|
if (browser) {
|
||||||
goto(`/dashboard/services`, { replaceState: true });
|
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>
|
</script>
|
||||||
|
|
||||||
{#await loadServiceConfig()}
|
{#await loadServiceConfig()}
|
||||||
@@ -38,7 +32,18 @@
|
|||||||
{:then}
|
{:then}
|
||||||
<div class="min-h-full text-white">
|
<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 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>
|
||||||
|
{:else if $page.params.name === 'code-server'}
|
||||||
|
<div>VSCode Server</div>
|
||||||
|
{:else if $page.params.name === 'minio'}
|
||||||
|
<div>MinIO</div>
|
||||||
|
{:else if $page.params.name.match(/wp-/)}
|
||||||
|
<div>Wordpress<span class="flex text-xs items-center justify-center">({service.config.baseURL.replace('https://','')})</span></div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="px-4">
|
<div class="px-4">
|
||||||
{#if $page.params.name === 'plausible'}
|
{#if $page.params.name === 'plausible'}
|
||||||
<img
|
<img
|
||||||
@@ -46,37 +51,75 @@
|
|||||||
class="w-6 mx-auto"
|
class="w-6 mx-auto"
|
||||||
src="https://cdn.coollabs.io/assets/coolify/services/plausible/logo_sm.png"
|
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"
|
||||||
|
/>
|
||||||
|
{:else if $page.params.name === 'code-server'}
|
||||||
|
<svg class="w-8 mx-auto" viewBox="0 0 128 128">
|
||||||
|
<path
|
||||||
|
d="M3.656 45.043s-3.027-2.191.61-5.113l8.468-7.594s2.426-2.559 4.989-.328l78.175 59.328v28.45s-.039 4.468-5.757 3.976zm0 0"
|
||||||
|
fill="#2489ca"
|
||||||
|
/><path
|
||||||
|
d="M23.809 63.379L3.656 81.742s-2.07 1.543 0 4.305l9.356 8.527s2.222 2.395 5.508-.328l21.359-16.238zm0 0"
|
||||||
|
fill="#1070b3"
|
||||||
|
/><path
|
||||||
|
d="M59.184 63.531l36.953-28.285-.239-28.297S94.32.773 89.055 3.99L39.879 48.851zm0 0"
|
||||||
|
fill="#0877b9"
|
||||||
|
/><path
|
||||||
|
d="M90.14 123.797c2.145 2.203 4.747 1.48 4.747 1.48l28.797-14.222c3.687-2.52 3.171-5.645 3.171-5.645V20.465c0-3.735-3.812-5.024-3.812-5.024L98.082 3.38c-5.453-3.379-9.027.61-9.027.61s4.593-3.317 6.843 2.96v112.317c0 .773-.164 1.53-.492 2.214-.656 1.332-2.086 2.57-5.504 2.051zm0 0"
|
||||||
|
fill="#3c99d4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else if $page.params.name === 'minio'}
|
||||||
|
<img
|
||||||
|
alt="minio logo"
|
||||||
|
class="w-7 mx-auto"
|
||||||
|
src="https://cdn.coollabs.io/assets/coolify/services/minio/MINIO_Bird.png"
|
||||||
|
/>
|
||||||
|
{:else if $page.params.name.match(/wp-/)}
|
||||||
|
<svg class="w-8 mx-auto" viewBox="0 0 128 128">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="white"
|
||||||
|
d="M64.094 126.224c34.275-.052 62.021-27.933 62.021-62.325 0-33.833-27.618-61.697-60.613-62.286C30.85.995 1.894 29.113 1.885 63.21c-.01 35.079 27.612 63.064 62.209 63.014zM63.993 4.63c32.907-.011 59.126 26.725 59.116 60.28-.011 31.679-26.925 58.18-59.092 58.187-32.771.007-59.125-26.563-59.124-59.608.002-32.193 26.766-58.848 59.1-58.859zM39.157 35.896c.538 1.793-.968 2.417-2.569 2.542-1.685.13-3.369.257-5.325.406 6.456 19.234 12.815 38.183 19.325 57.573.464-.759.655-.973.739-1.223 3.574-10.682 7.168-21.357 10.651-32.069.318-.977.16-2.271-.188-3.275-1.843-5.32-4.051-10.524-5.667-15.908-1.105-3.686-2.571-6.071-6.928-5.644-.742.073-1.648-1.524-2.479-2.349 1.005-.6 2.003-1.704 3.017-1.719a849.593 849.593 0 0126.618.008c1.018.017 2.016 1.15 3.021 1.765-.88.804-1.639 2.01-2.668 2.321-1.651.498-3.482.404-5.458.58l19.349 57.56c2.931-9.736 5.658-18.676 8.31-27.639 2.366-8.001.956-15.473-3.322-22.52-1.286-2.119-2.866-4.175-3.595-6.486-.828-2.629-1.516-5.622-1.077-8.259.745-4.469 4.174-6.688 8.814-7.113C74.333.881 34.431 9.317 19.728 34.922c5.66-.261 11.064-.604 16.472-.678 1.022-.013 2.717.851 2.957 1.652zm10.117 77.971c-.118.345-.125.729-.218 1.302 10.943 3.034 21.675 2.815 32.659-.886l-16.78-45.96c-5.37 15.611-10.52 30.575-15.661 45.544zm-8.456-2.078l-25.281-69.35c-11.405 22.278-2.729 56.268 25.281 69.35zm76.428-44.562c.802-10.534-2.832-25.119-5.97-27.125-.35 3.875-.106 8.186-1.218 12.114-2.617 9.255-5.817 18.349-8.899 27.468-3.35 9.912-6.832 19.779-10.257 29.666 16.092-9.539 24.935-23.618 26.344-42.123z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a target="_blank" class="icon mx-2" href={service.config.baseURL}>
|
||||||
target="_blank"
|
<svg
|
||||||
class="icon mx-2"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
href={service.config.baseURL}
|
class="h-6 w-6"
|
||||||
>
|
fill="none"
|
||||||
<svg
|
viewBox="0 0 24 24"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
stroke="currentColor"
|
||||||
class="h-6 w-6"
|
>
|
||||||
fill="none"
|
<path
|
||||||
viewBox="0 0 24 24"
|
stroke-linecap="round"
|
||||||
stroke="currentColor"
|
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>
|
</div>
|
||||||
<div class="space-y-2 max-w-4xl mx-auto px-6" in:fade={{ duration: 100 }}>
|
<div class="space-y-2 max-w-4xl mx-auto px-6" in:fade={{ duration: 100 }}>
|
||||||
<div class="block text-center py-4">
|
<div class="block text-center py-4">
|
||||||
{#if $page.params.name === 'plausible'}
|
{#if $page.params.name === 'plausible'}
|
||||||
<Plausible {service} />
|
<Plausible {service} />
|
||||||
|
{:else if $page.params.name === 'nocodb'}
|
||||||
|
<div class="font-bold">Nothing to show here. Enjoy using NocoDB!</div>
|
||||||
|
{:else if $page.params.name === 'code-server'}
|
||||||
|
<CodeServer {service} />
|
||||||
|
{:else if $page.params.name === 'minio'}
|
||||||
|
<MinIo {service} />
|
||||||
|
{:else if $page.params.name.match(/wp-/)}
|
||||||
|
<div class="font-bold">Nothing to show here. Enjoy using WordPress!</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
|
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
import { page, session } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import Loading from '$components/Loading.svelte';
|
import Loading from '$components/Loading.svelte';
|
||||||
import { request } from '$lib/request';
|
import { request } from '$lib/request';
|
||||||
@@ -14,13 +12,11 @@
|
|||||||
async function checkService() {
|
async function checkService() {
|
||||||
try {
|
try {
|
||||||
const data = await request(`/api/v1/services/${$page.params.type}`, $session);
|
const data = await request(`/api/v1/services/${$page.params.type}`, $session);
|
||||||
if (!data?.success) {
|
if (data?.success) {
|
||||||
if (browser) {
|
if (browser) {
|
||||||
goto(`/dashboard/services`, { replaceState: true });
|
goto(`/service/${$page.params.type}/configuration`, { replaceState: true });
|
||||||
toast.push(
|
toast.push(
|
||||||
`${
|
`Service already deployed.`
|
||||||
$page.params.type === 'plausible' ? 'Plausible Analytics' : $page.params.type
|
|
||||||
} already deployed.`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
import { toast } from '@zerodevx/svelte-toast';
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
import { newService } from '$store';
|
import { newService, newWordpressService } from '$store';
|
||||||
import { page, session } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import { request } from '$lib/request';
|
import { request } from '$lib/request';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
import TooltipInfo from '$components/TooltipInfo.svelte';
|
import TooltipInfo from '$components/TooltipInfo.svelte';
|
||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
|
|
||||||
$: deployable =
|
$: deployablePlausible =
|
||||||
$newService.baseURL === '' ||
|
$newService.baseURL === '' ||
|
||||||
$newService.baseURL === null ||
|
$newService.baseURL === null ||
|
||||||
$newService.email === '' ||
|
$newService.email === '' ||
|
||||||
@@ -21,8 +21,13 @@
|
|||||||
$newService.userPassword === null ||
|
$newService.userPassword === null ||
|
||||||
$newService.userPassword.length <= 6 ||
|
$newService.userPassword.length <= 6 ||
|
||||||
$newService.userPassword !== $newService.userPasswordAgain;
|
$newService.userPassword !== $newService.userPasswordAgain;
|
||||||
|
|
||||||
|
$: deployableNocoDB = $newService.baseURL === '' || $newService.baseURL === null;
|
||||||
|
$: deployableCodeServer = $newService.baseURL === '' || $newService.baseURL === null;
|
||||||
|
$: deployableMinIO = $newService.baseURL === '' || $newService.baseURL === null;
|
||||||
|
$: deployableWordpress = false
|
||||||
let loading = false;
|
let loading = false;
|
||||||
async function deploy() {
|
async function deployPlausible() {
|
||||||
try {
|
try {
|
||||||
loading = true;
|
loading = true;
|
||||||
const payload = $newService;
|
const payload = $newService;
|
||||||
@@ -44,6 +49,95 @@
|
|||||||
loading = false;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function deployCodeServer() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function deployMinIO() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function deployWordpress() {
|
||||||
|
try {
|
||||||
|
loading = true;
|
||||||
|
await request(`/api/v1/services/deploy/${$page.params.type}`, $session, {
|
||||||
|
body: {
|
||||||
|
...$newWordpressService
|
||||||
|
}
|
||||||
|
});
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="min-h-full text-white">
|
<div class="min-h-full text-white">
|
||||||
@@ -51,18 +145,26 @@
|
|||||||
Deploy new
|
Deploy new
|
||||||
{#if $page.params.type === 'plausible'}
|
{#if $page.params.type === 'plausible'}
|
||||||
<span class="text-blue-500 px-2 capitalize">Plausible Analytics</span>
|
<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>
|
||||||
|
{:else if $page.params.type === 'code-server'}
|
||||||
|
<span class="text-blue-500 px-2 capitalize">VSCode Server</span>
|
||||||
|
{:else if $page.params.type === 'minio'}
|
||||||
|
<span class="text-blue-500 px-2 capitalize">MinIO</span>
|
||||||
|
{:else if $page.params.type === 'wordpress'}
|
||||||
|
<span class="text-blue-500 px-2 capitalize">Wordpress</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<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="space-y-2 max-w-xl mx-auto px-6 flex-col text-center" in:fade={{ duration: 100 }}>
|
||||||
<div class="grid grid-flow-row">
|
<div class="grid grid-flow-row">
|
||||||
<label for="Domain"
|
<label for="Domain"
|
||||||
>Domain <TooltipInfo
|
>Domain <TooltipInfo
|
||||||
position="right"
|
position="right"
|
||||||
label={`You will have your Plausible instance at here.`}
|
label={`You could reach your Plausible Analytics instance here.`}
|
||||||
/></label
|
/></label
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@@ -114,15 +216,292 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
disabled={deployable}
|
disabled={deployablePlausible}
|
||||||
class:cursor-not-allowed={deployable}
|
class:cursor-not-allowed={deployablePlausible}
|
||||||
class:bg-blue-500={!deployable}
|
class:bg-blue-500={!deployablePlausible}
|
||||||
class:hover:bg-blue-400={!deployable}
|
class:hover:bg-blue-400={!deployablePlausible}
|
||||||
class:hover:bg-transparent={deployable}
|
class:hover:bg-transparent={deployablePlausible}
|
||||||
class:text-warmGray-700={deployable}
|
class:text-warmGray-700={deployablePlausible}
|
||||||
class:text-white={!deployable}
|
class:text-white={!deployablePlausible}
|
||||||
class="button p-2"
|
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
|
||||||
|
disabled={deployableNocoDB}
|
||||||
|
class:cursor-not-allowed={deployableNocoDB}
|
||||||
|
class:bg-blue-500={!deployableNocoDB}
|
||||||
|
class:hover:bg-blue-400={!deployableNocoDB}
|
||||||
|
class:hover:bg-transparent={deployableNocoDB}
|
||||||
|
class:text-warmGray-700={deployableNocoDB}
|
||||||
|
class:text-white={!deployableNocoDB}
|
||||||
|
class="button p-2 w-64 bg-blue-500 hover:bg-blue-400 text-white"
|
||||||
|
on:click={deployNocodb}
|
||||||
|
>
|
||||||
|
Deploy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{:else if $page.params.type === 'code-server'}
|
||||||
|
<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 Code Server instance here.`}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="Domain"
|
||||||
|
class:border-red-500={$newService.baseURL == null || $newService.baseURL == ''}
|
||||||
|
bind:value={$newService.baseURL}
|
||||||
|
placeholder="code.coollabs.io"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
disabled={deployableCodeServer}
|
||||||
|
class:cursor-not-allowed={deployableCodeServer}
|
||||||
|
class:bg-blue-500={!deployableCodeServer}
|
||||||
|
class:hover:bg-blue-400={!deployableCodeServer}
|
||||||
|
class:hover:bg-transparent={deployableCodeServer}
|
||||||
|
class:text-warmGray-700={deployableCodeServer}
|
||||||
|
class:text-white={!deployableCodeServer}
|
||||||
|
class="button p-2 w-64 bg-blue-500 hover:bg-blue-400 text-white"
|
||||||
|
on:click={deployCodeServer}
|
||||||
|
>
|
||||||
|
Deploy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{:else if $page.params.type === 'minio'}
|
||||||
|
<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 MinIO instance here.`}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="Domain"
|
||||||
|
class:border-red-500={$newService.baseURL == null || $newService.baseURL == ''}
|
||||||
|
bind:value={$newService.baseURL}
|
||||||
|
placeholder="minio.coollabs.io"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
disabled={deployableMinIO}
|
||||||
|
class:cursor-not-allowed={deployableMinIO}
|
||||||
|
class:bg-blue-500={!deployableMinIO}
|
||||||
|
class:hover:bg-blue-400={!deployableMinIO}
|
||||||
|
class:hover:bg-transparent={deployableMinIO}
|
||||||
|
class:text-warmGray-700={deployableMinIO}
|
||||||
|
class:text-white={!deployableMinIO}
|
||||||
|
class="button p-2 w-64 bg-blue-500 hover:bg-blue-400 text-white"
|
||||||
|
on:click={deployMinIO}
|
||||||
|
>
|
||||||
|
Deploy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{:else if $page.params.type === 'wordpress'}
|
||||||
|
<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 Wordpress instance here.`}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="Domain"
|
||||||
|
class:border-red-500={$newWordpressService.baseURL == null || $newWordpressService.baseURL == ''}
|
||||||
|
bind:value={$newWordpressService.baseURL}
|
||||||
|
placeholder="wordpress.coollabs.io"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<div class="px-4 sm:px-6">
|
||||||
|
<ul class="divide-y divide-warmGray-800">
|
||||||
|
<li class="py-4 flex items-center justify-between text-left">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<p class="text-base font-bold text-warmGray-100">Use remote MySQL database?</p>
|
||||||
|
<p class="text-sm font-medium text-warmGray-400">
|
||||||
|
If not, Coolify will create a local database for you.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click={() => ($newWordpressService.remoteDB = !$newWordpressService.remoteDB)}
|
||||||
|
aria-pressed="true"
|
||||||
|
class="relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200"
|
||||||
|
class:bg-green-600={$newWordpressService.remoteDB}
|
||||||
|
class:bg-warmGray-700={!$newWordpressService.remoteDB}
|
||||||
|
>
|
||||||
|
<span class="sr-only">Use setting</span>
|
||||||
|
<span
|
||||||
|
class="pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transition ease-in-out duration-200 transform"
|
||||||
|
class:translate-x-5={$newWordpressService.remoteDB}
|
||||||
|
class:translate-x-0={!$newWordpressService.remoteDB}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class=" ease-in duration-200 absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"
|
||||||
|
class:opacity-0={$newWordpressService.remoteDB}
|
||||||
|
class:opacity-100={!$newWordpressService.remoteDB}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<svg class="bg-white h-3 w-3 text-red-600" fill="none" viewBox="0 0 12 12">
|
||||||
|
<path
|
||||||
|
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="ease-out duration-100 absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"
|
||||||
|
aria-hidden="true"
|
||||||
|
class:opacity-100={$newWordpressService.remoteDB}
|
||||||
|
class:opacity-0={!$newWordpressService.remoteDB}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="bg-white h-3 w-3 text-green-600"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 12 12"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{#if $newWordpressService.remoteDB}
|
||||||
|
<div class="grid grid-flow-row pb-5">
|
||||||
|
<label for="database.host"
|
||||||
|
>DB Host <TooltipInfo
|
||||||
|
position="right"
|
||||||
|
label={`IP address of a remote Mysql instance.`}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="database.host"
|
||||||
|
class:border-red-500={$newWordpressService.database.host == null ||
|
||||||
|
$newWordpressService.database.host == ''}
|
||||||
|
bind:value={$newWordpressService.database.host}
|
||||||
|
placeholder="10.10.10.10:3306"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-flow-row pb-5">
|
||||||
|
<label for="database.user"
|
||||||
|
>DB User <TooltipInfo position="right" label={`Database user.`} /></label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="database.user"
|
||||||
|
class:border-red-500={$newWordpressService.database.user == null ||
|
||||||
|
$newWordpressService.database.user == ''}
|
||||||
|
bind:value={$newWordpressService.database.user}
|
||||||
|
placeholder="wordpressuser"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-flow-row pb-5">
|
||||||
|
<label for="database.password"
|
||||||
|
>DB Password <TooltipInfo
|
||||||
|
position="right"
|
||||||
|
label={`Database password for the database user.`}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="database.password"
|
||||||
|
class:border-red-500={$newWordpressService.database.password == null ||
|
||||||
|
$newWordpressService.database.password == ''}
|
||||||
|
bind:value={$newWordpressService.database.password}
|
||||||
|
placeholder="supersecretuserpasswordforwordpress"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-flow-row pb-5">
|
||||||
|
<label for="database.name"
|
||||||
|
>DB Name<TooltipInfo
|
||||||
|
position="right"
|
||||||
|
label={`Database name`}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="database.name"
|
||||||
|
class:border-red-500={$newWordpressService.database.name == null ||
|
||||||
|
$newWordpressService.database.name == ''}
|
||||||
|
bind:value={$newWordpressService.database.name}
|
||||||
|
placeholder="wordpress"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-flow-row pb-5">
|
||||||
|
<label for="database.tablePrefix"
|
||||||
|
>DB Table Prefix <TooltipInfo
|
||||||
|
position="right"
|
||||||
|
label={`Table prefix for wordpress`}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="database.tablePrefix"
|
||||||
|
class:border-red-500={$newWordpressService.database.tablePrefix == null ||
|
||||||
|
$newWordpressService.database.tablePrefix == ''}
|
||||||
|
bind:value={$newWordpressService.database.tablePrefix}
|
||||||
|
placeholder="wordpress"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="grid grid-flow-row py-5">
|
||||||
|
<label for="wordpressExtraConfiguration"
|
||||||
|
>Wordpress Configuration Extra <TooltipInfo
|
||||||
|
position="right"
|
||||||
|
label={`Database password for the database user.`}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
class="h-32"
|
||||||
|
id="wordpressExtraConfiguration"
|
||||||
|
bind:value={$newWordpressService.wordpressExtraConfiguration}
|
||||||
|
placeholder="// Example Extra Configuration
|
||||||
|
define('WP_ALLOW_MULTISITE', true );
|
||||||
|
define('MULTISITE', true);
|
||||||
|
define('SUBDOMAIN_INSTALL', false);"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
disabled={deployableWordpress}
|
||||||
|
class:cursor-not-allowed={deployableWordpress}
|
||||||
|
class:bg-blue-500={!deployableWordpress}
|
||||||
|
class:hover:bg-blue-400={!deployableWordpress}
|
||||||
|
class:hover:bg-transparent={deployableWordpress}
|
||||||
|
class:text-warmGray-700={deployableWordpress}
|
||||||
|
class:text-white={!deployableWordpress}
|
||||||
|
class="button p-2 w-64 bg-blue-500 hover:bg-blue-400 text-white"
|
||||||
|
on:click={deployWordpress}
|
||||||
>
|
>
|
||||||
Deploy
|
Deploy
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script>
|
<script>
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="min-h-full text-white">
|
<div class="min-h-full text-white">
|
||||||
@@ -10,11 +10,11 @@
|
|||||||
Select a service
|
Select a service
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center space-y-2 max-w-4xl mx-auto px-6" in:fade={{ duration: 100 }}>
|
<div class="text-center space-y-2 max-w-7xl mx-auto px-6" in:fade={{ duration: 100 }}>
|
||||||
{#if $page.path === '/service/new'}
|
{#if $page.path === '/service/new'}
|
||||||
<div class="flex justify-center space-x-4 font-bold pb-6">
|
<div class="flex justify-center font-bold pb-6 flex-wrap">
|
||||||
<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-blue-500 p-2 rounded bg-warmGray-800"
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-500 p-2 m-2 rounded bg-warmGray-800 w-48"
|
||||||
on:click={() => goto('/service/new/plausible')}
|
on:click={() => goto('/service/new/plausible')}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@@ -24,6 +24,67 @@
|
|||||||
/>
|
/>
|
||||||
<div class="text-white">Plausible Analytics</div>
|
<div class="text-white">Plausible Analytics</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-500 p-2 m-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
|
||||||
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-500 p-2 m-2 rounded bg-warmGray-800 w-48"
|
||||||
|
on:click={() => goto('/service/new/code-server')}
|
||||||
|
>
|
||||||
|
<svg class="w-14 mx-auto pb-2" viewBox="0 0 128 128">
|
||||||
|
<path
|
||||||
|
d="M3.656 45.043s-3.027-2.191.61-5.113l8.468-7.594s2.426-2.559 4.989-.328l78.175 59.328v28.45s-.039 4.468-5.757 3.976zm0 0"
|
||||||
|
fill="#2489ca"
|
||||||
|
/><path
|
||||||
|
d="M23.809 63.379L3.656 81.742s-2.07 1.543 0 4.305l9.356 8.527s2.222 2.395 5.508-.328l21.359-16.238zm0 0"
|
||||||
|
fill="#1070b3"
|
||||||
|
/><path
|
||||||
|
d="M59.184 63.531l36.953-28.285-.239-28.297S94.32.773 89.055 3.99L39.879 48.851zm0 0"
|
||||||
|
fill="#0877b9"
|
||||||
|
/><path
|
||||||
|
d="M90.14 123.797c2.145 2.203 4.747 1.48 4.747 1.48l28.797-14.222c3.687-2.52 3.171-5.645 3.171-5.645V20.465c0-3.735-3.812-5.024-3.812-5.024L98.082 3.38c-5.453-3.379-9.027.61-9.027.61s4.593-3.317 6.843 2.96v112.317c0 .773-.164 1.53-.492 2.214-.656 1.332-2.086 2.57-5.504 2.051zm0 0"
|
||||||
|
fill="#3c99d4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div class="flex-1" />
|
||||||
|
<div class="text-white">VSCode Server</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-500 p-2 m-2 rounded bg-warmGray-800 w-48"
|
||||||
|
on:click={() => goto('/service/new/minio')}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="minio logo"
|
||||||
|
class="w-7 mx-auto pb-2"
|
||||||
|
src="https://cdn.coollabs.io/assets/coolify/services/minio/MINIO_Bird.png"
|
||||||
|
/>
|
||||||
|
<div class="flex-1" />
|
||||||
|
<div class="text-white">MinIO</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-center flex-col items-center cursor-pointer ease-in-out hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-500 p-2 m-2 rounded bg-warmGray-800 w-48"
|
||||||
|
on:click={() => goto('/service/new/wordpress')}
|
||||||
|
>
|
||||||
|
<svg class="w-14 mx-auto pb-2" viewBox="0 0 128 128">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="white"
|
||||||
|
d="M64.094 126.224c34.275-.052 62.021-27.933 62.021-62.325 0-33.833-27.618-61.697-60.613-62.286C30.85.995 1.894 29.113 1.885 63.21c-.01 35.079 27.612 63.064 62.209 63.014zM63.993 4.63c32.907-.011 59.126 26.725 59.116 60.28-.011 31.679-26.925 58.18-59.092 58.187-32.771.007-59.125-26.563-59.124-59.608.002-32.193 26.766-58.848 59.1-58.859zM39.157 35.896c.538 1.793-.968 2.417-2.569 2.542-1.685.13-3.369.257-5.325.406 6.456 19.234 12.815 38.183 19.325 57.573.464-.759.655-.973.739-1.223 3.574-10.682 7.168-21.357 10.651-32.069.318-.977.16-2.271-.188-3.275-1.843-5.32-4.051-10.524-5.667-15.908-1.105-3.686-2.571-6.071-6.928-5.644-.742.073-1.648-1.524-2.479-2.349 1.005-.6 2.003-1.704 3.017-1.719a849.593 849.593 0 0126.618.008c1.018.017 2.016 1.15 3.021 1.765-.88.804-1.639 2.01-2.668 2.321-1.651.498-3.482.404-5.458.58l19.349 57.56c2.931-9.736 5.658-18.676 8.31-27.639 2.366-8.001.956-15.473-3.322-22.52-1.286-2.119-2.866-4.175-3.595-6.486-.828-2.629-1.516-5.622-1.077-8.259.745-4.469 4.174-6.688 8.814-7.113C74.333.881 34.431 9.317 19.728 34.922c5.66-.261 11.064-.604 16.472-.678 1.022-.013 2.717.851 2.957 1.652zm10.117 77.971c-.118.345-.125.729-.218 1.302 10.943 3.034 21.675 2.815 32.659-.886l-16.78-45.96c-5.37 15.611-10.52 30.575-15.661 45.544zm-8.456-2.078l-25.281-69.35c-11.405 22.278-2.729 56.268 25.281 69.35zm76.428-44.562c.802-10.534-2.832-25.119-5.97-27.125-.35 3.875-.106 8.186-1.218 12.114-2.617 9.255-5.817 18.349-8.899 27.468-3.35 9.912-6.832 19.779-10.257 29.666 16.092-9.539 24.935-23.618 26.344-42.123z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div class="flex-1" />
|
||||||
|
<div class="text-white">Wordpress</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
>
|
>
|
||||||
<span class="sr-only">Use setting</span>
|
<span class="sr-only">Use setting</span>
|
||||||
<span
|
<span
|
||||||
class="pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"
|
class="pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transition ease-in-out duration-200 transform"
|
||||||
class:translate-x-5={settings?.allowRegistration}
|
class:translate-x-5={settings?.allowRegistration}
|
||||||
class:translate-x-0={!settings?.allowRegistration}
|
class:translate-x-0={!settings?.allowRegistration}
|
||||||
>
|
>
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
>
|
>
|
||||||
<span class="sr-only">Use setting</span>
|
<span class="sr-only">Use setting</span>
|
||||||
<span
|
<span
|
||||||
class="pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"
|
class="pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transition ease-in-out duration-200 transform"
|
||||||
class:translate-x-5={settings?.sendErrors}
|
class:translate-x-5={settings?.sendErrors}
|
||||||
class:translate-x-0={!settings?.sendErrors}
|
class:translate-x-0={!settings?.sendErrors}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export const application = writable<Application>({
|
|||||||
secrets: []
|
secrets: []
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
export const prApplication = writable([])
|
export const prApplication = writable([]);
|
||||||
|
|
||||||
export const initConf = writable({});
|
export const initConf = writable({});
|
||||||
|
|
||||||
@@ -167,5 +167,17 @@ export const initialNewService = {
|
|||||||
userPasswordAgain: null,
|
userPasswordAgain: null,
|
||||||
baseURL: null
|
baseURL: null
|
||||||
};
|
};
|
||||||
|
export const newWordpressService = writable({
|
||||||
|
baseURL: null,
|
||||||
|
remoteDB: false,
|
||||||
|
database: {
|
||||||
|
host: null,
|
||||||
|
name: 'wordpress',
|
||||||
|
user: null,
|
||||||
|
password: null,
|
||||||
|
tablePrefix: 'wordpress'
|
||||||
|
},
|
||||||
|
wordpressExtraConfiguration: null
|
||||||
|
});
|
||||||
|
|
||||||
export const isPullRequestPermissionsGranted = writable(false)
|
export const isPullRequestPermissionsGranted = writable(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user