commit
22f1a3c908
47
.github/ISSUE_TEMPLATE/--bug-report.yaml
vendored
Normal file
47
.github/ISSUE_TEMPLATE/--bug-report.yaml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
name: 🐞 Bug report
|
||||
description: Create a bug report to help us improve coolify
|
||||
title: "[Bug]: "
|
||||
labels: [Bug]
|
||||
assignees:
|
||||
- andrasbacsai
|
||||
- vasani-arpit
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please fill the form in English
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: A concise description of what you're experiencing and what you expect.
|
||||
placeholder: |
|
||||
When I do <X>, <Y> happens and I see the error message attached below:
|
||||
```...```
|
||||
What I expect is <Z>
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps To Reproduce
|
||||
description: Add steps to reproduce this behaviour, include console / network logs & videos
|
||||
placeholder: |
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: "The version of your coolify Instance"
|
||||
placeholder: "2.5.2"
|
||||
validations:
|
||||
required: true
|
31
.github/ISSUE_TEMPLATE/--feature-request.yaml
vendored
Normal file
31
.github/ISSUE_TEMPLATE/--feature-request.yaml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: 🛠️ Feature request
|
||||
description: Suggest an idea to improve coolify
|
||||
title: '[Feature]: '
|
||||
labels: [Enhancement]
|
||||
assignees:
|
||||
- andrasbacsai
|
||||
- vasani-arpit
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to request a feature for coolify! Please also add your request here to get feedback from the community: https://feedback.coolify.io/!
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue related to this feature request already exists.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: One paragraph description of the feature.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Why should this be worked on?
|
||||
description: A concise description of the problems or use cases for this feature request.
|
||||
validations:
|
||||
required: true
|
20
.github/ISSUE_TEMPLATE/--task.yaml
vendored
Normal file
20
.github/ISSUE_TEMPLATE/--task.yaml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: 📝 Task
|
||||
description: Create a task for the team to work on
|
||||
title: "[Task]: "
|
||||
labels: [Task]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue related to this already exists.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: SubTasks
|
||||
placeholder: |
|
||||
- Sub Task 1
|
||||
- Sub Task 2
|
||||
validations:
|
||||
required: false
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: 🤔 Questions and Help
|
||||
url: https://discord.com/invite/6rDM4fkymF
|
||||
about: Reach out to us on discord or our github discussions page.
|
@ -55,7 +55,6 @@ These are the predefined build packs, but with the Docker build pack, you can ho
|
||||
- NuxtJS
|
||||
- NextJS
|
||||
- React/Preact
|
||||
- NextJS
|
||||
- Gatsby
|
||||
- Svelte
|
||||
- PHP
|
||||
@ -68,6 +67,7 @@ These are the predefined build packs, but with the Docker build pack, you can ho
|
||||
One-click database is ready to be used internally or shared over the internet:
|
||||
|
||||
- MongoDB
|
||||
- MariaDB
|
||||
- MySQL
|
||||
- PostgreSQL
|
||||
- CouchDB
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coolify",
|
||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||
"version": "2.6.3",
|
||||
"version": "2.7.0",
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0",
|
||||
@ -77,6 +77,7 @@
|
||||
"generate-password": "1.7.0",
|
||||
"get-port": "6.1.2",
|
||||
"got": "12.0.3",
|
||||
"is-ip": "^4.0.0",
|
||||
"js-cookie": "3.0.1",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
|
64
pnpm-lock.yaml
generated
64
pnpm-lock.yaml
generated
@ -1,4 +1,4 @@
|
||||
lockfileVersion: 5.3
|
||||
lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@iarna/toml': 2.2.5
|
||||
@ -31,6 +31,7 @@ specifiers:
|
||||
get-port: 6.1.2
|
||||
got: 12.0.3
|
||||
husky: 7.0.4
|
||||
is-ip: ^4.0.0
|
||||
js-cookie: 3.0.1
|
||||
js-yaml: 4.1.0
|
||||
jsonwebtoken: 8.5.1
|
||||
@ -71,6 +72,7 @@ dependencies:
|
||||
generate-password: 1.7.0
|
||||
get-port: 6.1.2
|
||||
got: 12.0.3
|
||||
is-ip: 4.0.0
|
||||
js-cookie: 3.0.1
|
||||
js-yaml: 4.1.0
|
||||
jsonwebtoken: 8.5.1
|
||||
@ -88,29 +90,29 @@ devDependencies:
|
||||
'@types/js-yaml': 4.0.5
|
||||
'@types/node': 17.0.25
|
||||
'@types/node-forge': 1.0.1
|
||||
'@typescript-eslint/eslint-plugin': 4.31.1_8ede7edd7694646e12d33c52460f622c
|
||||
'@typescript-eslint/parser': 4.31.1_eslint@7.32.0+typescript@4.6.3
|
||||
'@typescript-eslint/eslint-plugin': 4.31.1_r3ph5xlwsrsg4ewthrjemd3cfq
|
||||
'@typescript-eslint/parser': 4.31.1_hrkuebk64jiu2ut2d2sm4oylnu
|
||||
'@zerodevx/svelte-toast': 0.7.1
|
||||
autoprefixer: 10.4.4_postcss@8.4.12
|
||||
cross-env: 7.0.3
|
||||
cross-var: 1.1.0
|
||||
eslint: 7.32.0
|
||||
eslint-config-prettier: 8.5.0_eslint@7.32.0
|
||||
eslint-plugin-svelte3: 3.4.1_eslint@7.32.0+svelte@3.47.0
|
||||
eslint-plugin-svelte3: 3.4.1_4oxeyilw5mxcaksmcxtpjddhfe
|
||||
husky: 7.0.4
|
||||
lint-staged: 12.4.0
|
||||
postcss: 8.4.12
|
||||
prettier: 2.6.2
|
||||
prettier-plugin-svelte: 2.7.0_prettier@2.6.2+svelte@3.47.0
|
||||
prettier-plugin-svelte: 2.7.0_sqtt6dzjlskmywoml5ykunxlce
|
||||
prettier-plugin-tailwindcss: 0.1.10_prettier@2.6.2
|
||||
prisma: 3.11.1
|
||||
svelte: 3.47.0
|
||||
svelte-check: 2.7.0_postcss@8.4.12+svelte@3.47.0
|
||||
svelte-preprocess: 4.10.6_41810887ae6c6d59323116f47e33fa38
|
||||
svelte-check: 2.7.0_cp6olp7pwsfaq5mjijwt65d6uy
|
||||
svelte-preprocess: 4.10.6_igaqrb5onrwvsmrrc32h4m72ha
|
||||
svelte-select: 4.4.7
|
||||
sveltekit-i18n: 2.1.2_svelte@3.47.0
|
||||
tailwindcss: 3.0.24_ts-node@10.7.0
|
||||
ts-node: 10.7.0_de7c86b0cde507c63a0402da5b982bd3
|
||||
ts-node: 10.7.0_3z6inmgn4ud4moqealnfxgbl2m
|
||||
tslib: 2.3.1
|
||||
typescript: 4.6.3
|
||||
|
||||
@ -571,7 +573,7 @@ packages:
|
||||
'@types/node': 17.0.25
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/4.31.1_8ede7edd7694646e12d33c52460f622c:
|
||||
/@typescript-eslint/eslint-plugin/4.31.1_r3ph5xlwsrsg4ewthrjemd3cfq:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-UDqhWmd5i0TvPLmbK5xY3UZB0zEGseF+DHPghZ37Sb83Qd3p8ujhvAtkU4OF46Ka5Pm5kWvFIx0cCTBFKo0alA==
|
||||
@ -585,8 +587,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/experimental-utils': 4.31.1_eslint@7.32.0+typescript@4.6.3
|
||||
'@typescript-eslint/parser': 4.31.1_eslint@7.32.0+typescript@4.6.3
|
||||
'@typescript-eslint/experimental-utils': 4.31.1_hrkuebk64jiu2ut2d2sm4oylnu
|
||||
'@typescript-eslint/parser': 4.31.1_hrkuebk64jiu2ut2d2sm4oylnu
|
||||
'@typescript-eslint/scope-manager': 4.31.1
|
||||
debug: 4.3.3
|
||||
eslint: 7.32.0
|
||||
@ -599,7 +601,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/experimental-utils/4.31.1_eslint@7.32.0+typescript@4.6.3:
|
||||
/@typescript-eslint/experimental-utils/4.31.1_hrkuebk64jiu2ut2d2sm4oylnu:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-NtoPsqmcSsWty0mcL5nTZXMf7Ei0Xr2MT8jWjXMVgRK0/1qeQ2jZzLFUh4QtyJ4+/lPUyMw5cSfeeME+Zrtp9Q==
|
||||
@ -620,7 +622,7 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser/4.31.1_eslint@7.32.0+typescript@4.6.3:
|
||||
/@typescript-eslint/parser/4.31.1_hrkuebk64jiu2ut2d2sm4oylnu:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-dnVZDB6FhpIby6yVbHkwTKkn2ypjVIfAR9nh+kYsA/ZL0JlTsd22BiDjouotisY3Irmd3OW1qlk9EI5R8GrvRQ==
|
||||
@ -2613,7 +2615,7 @@ packages:
|
||||
eslint: 7.32.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-svelte3/3.4.1_eslint@7.32.0+svelte@3.47.0:
|
||||
/eslint-plugin-svelte3/3.4.1_4oxeyilw5mxcaksmcxtpjddhfe:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-7p59WG8qV8L6wLdl4d/c3mdjkgVglQCdv5XOTk/iNPBKXuuV+Q0eFP5Wa6iJd/G2M1qR3BkLPEzaANOqKAZczw==
|
||||
@ -3283,6 +3285,14 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/ip-regex/5.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==
|
||||
}
|
||||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||
dev: false
|
||||
|
||||
/is-binary-path/2.1.0:
|
||||
resolution:
|
||||
{
|
||||
@ -3341,6 +3351,16 @@ packages:
|
||||
is-extglob: 2.1.1
|
||||
dev: true
|
||||
|
||||
/is-ip/4.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-4B4XA2HEIm/PY+OSpeMBXr8pGWBYbXuHgjMAqrwbLO3CPTCAd9ArEJzBUKGZtk9viY6+aSfadGnWyjY3ydYZkw==
|
||||
}
|
||||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||
dependencies:
|
||||
ip-regex: 5.0.0
|
||||
dev: false
|
||||
|
||||
/is-number/7.0.0:
|
||||
resolution:
|
||||
{
|
||||
@ -4103,7 +4123,7 @@ packages:
|
||||
postcss: 8.4.12
|
||||
dev: true
|
||||
|
||||
/postcss-load-config/3.1.4_postcss@8.4.12+ts-node@10.7.0:
|
||||
/postcss-load-config/3.1.4_ysmyu6g5dtd6yanj6zrab4uqoy:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
|
||||
@ -4120,7 +4140,7 @@ packages:
|
||||
dependencies:
|
||||
lilconfig: 2.0.5
|
||||
postcss: 8.4.12
|
||||
ts-node: 10.7.0_de7c86b0cde507c63a0402da5b982bd3
|
||||
ts-node: 10.7.0_3z6inmgn4ud4moqealnfxgbl2m
|
||||
yaml: 1.10.2
|
||||
dev: true
|
||||
|
||||
@ -4175,7 +4195,7 @@ packages:
|
||||
engines: { node: '>= 0.8.0' }
|
||||
dev: true
|
||||
|
||||
/prettier-plugin-svelte/2.7.0_prettier@2.6.2+svelte@3.47.0:
|
||||
/prettier-plugin-svelte/2.7.0_sqtt6dzjlskmywoml5ykunxlce:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==
|
||||
@ -4858,7 +4878,7 @@ packages:
|
||||
engines: { node: '>= 0.4' }
|
||||
dev: true
|
||||
|
||||
/svelte-check/2.7.0_postcss@8.4.12+svelte@3.47.0:
|
||||
/svelte-check/2.7.0_cp6olp7pwsfaq5mjijwt65d6uy:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-GrvG24j0+i8AOm0k0KyJ6Dqc+TAR2yzB7rtS4nljHStunVxCTr/1KYlv4EsOeoqtHLzeWMOd5D2O6nDdP/yw4A==
|
||||
@ -4874,7 +4894,7 @@ packages:
|
||||
sade: 1.7.4
|
||||
source-map: 0.7.3
|
||||
svelte: 3.47.0
|
||||
svelte-preprocess: 4.10.6_41810887ae6c6d59323116f47e33fa38
|
||||
svelte-preprocess: 4.10.6_igaqrb5onrwvsmrrc32h4m72ha
|
||||
typescript: 4.6.3
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
@ -4907,7 +4927,7 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/svelte-preprocess/4.10.6_41810887ae6c6d59323116f47e33fa38:
|
||||
/svelte-preprocess/4.10.6_igaqrb5onrwvsmrrc32h4m72ha:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==
|
||||
@ -5039,7 +5059,7 @@ packages:
|
||||
picocolors: 1.0.0
|
||||
postcss: 8.4.12
|
||||
postcss-js: 4.0.0_postcss@8.4.12
|
||||
postcss-load-config: 3.1.4_postcss@8.4.12+ts-node@10.7.0
|
||||
postcss-load-config: 3.1.4_ysmyu6g5dtd6yanj6zrab4uqoy
|
||||
postcss-nested: 5.0.6_postcss@8.4.12
|
||||
postcss-selector-parser: 6.0.10
|
||||
postcss-value-parser: 4.2.0
|
||||
@ -5113,7 +5133,7 @@ packages:
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
/ts-node/10.7.0_de7c86b0cde507c63a0402da5b982bd3:
|
||||
/ts-node/10.7.0_3z6inmgn4ud4moqealnfxgbl2m:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==
|
||||
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Application" ADD COLUMN "exposePort" INTEGER;
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Service" ADD COLUMN "exposePort" INTEGER;
|
@ -84,6 +84,7 @@ model Application {
|
||||
buildPack String?
|
||||
projectId Int?
|
||||
port Int?
|
||||
exposePort Int?
|
||||
installCommand String?
|
||||
buildCommand String?
|
||||
startCommand String?
|
||||
@ -289,6 +290,7 @@ model Service {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
fqdn String?
|
||||
exposePort Int?
|
||||
dualCerts Boolean @default(false)
|
||||
type String?
|
||||
version String?
|
||||
|
@ -2,7 +2,7 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, imageforBuild): Promise<void> => {
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId } = data;
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${imageforBuild}`);
|
||||
@ -12,7 +12,7 @@ const createDockerfile = async (data, imageforBuild): Promise<void> => {
|
||||
if (baseImage.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { buildCacheImageForLaravel, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const { workdir, applicationId, tag, buildId } = data;
|
||||
const { workdir, applicationId, tag, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
@ -24,7 +24,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
`COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/mix-manifest.json /app/public/mix-manifest.json`
|
||||
);
|
||||
Dockerfile.push(`COPY --chown=application:application . ./`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
||||
const { workdir, baseDirectory, buildId } = data;
|
||||
const { workdir, baseDirectory, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
let composerFound = false;
|
||||
try {
|
||||
@ -22,7 +22,7 @@ const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
||||
}
|
||||
|
||||
Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId } = data;
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
@ -12,7 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
if (baseImage.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
secrets,
|
||||
pullmergeRequestId,
|
||||
baseImage,
|
||||
buildId
|
||||
buildId,
|
||||
port
|
||||
} = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
@ -42,7 +43,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
if (baseImage.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId } = data;
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
@ -12,7 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
if (baseImage.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId } = data;
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
@ -12,7 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
if (baseImage.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,8 @@ import { dev } from '$app/env';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { uniqueNamesGenerator, adjectives, colors, animals } from 'unique-names-generator';
|
||||
import type { Config } from 'unique-names-generator';
|
||||
import { promises as dns } from 'dns';
|
||||
import { isIP } from 'is-ip';
|
||||
|
||||
import * as db from '$lib/database';
|
||||
import { buildLogQueue } from './queues';
|
||||
@ -14,6 +16,7 @@ import Cookie from 'cookie';
|
||||
import os from 'os';
|
||||
import type { RequestEvent } from '@sveltejs/kit/types/internal';
|
||||
import type { Job } from 'bullmq';
|
||||
import { t } from './translations';
|
||||
|
||||
try {
|
||||
if (!dev) {
|
||||
@ -179,3 +182,97 @@ export function getDomain(domain: string): string {
|
||||
export function getOsArch() {
|
||||
return os.arch();
|
||||
}
|
||||
|
||||
export async function isDNSValid(event: any, domain: string): Promise<any> {
|
||||
let resolves = [];
|
||||
try {
|
||||
if (isIP(event.url.hostname)) {
|
||||
resolves = [event.url.hostname];
|
||||
} else {
|
||||
resolves = await dns.resolve4(event.url.hostname);
|
||||
}
|
||||
} catch (error) {
|
||||
throw {
|
||||
message: t.get('application.dns_not_set_error', { domain })
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
let ipDomainFound = false;
|
||||
dns.setServers(['1.1.1.1', '8.8.8.8']);
|
||||
const dnsResolve = await dns.resolve4(domain);
|
||||
if (dnsResolve.length > 0) {
|
||||
for (const ip of dnsResolve) {
|
||||
if (resolves.includes(ip)) {
|
||||
ipDomainFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ipDomainFound) throw false;
|
||||
} catch (error) {
|
||||
throw {
|
||||
message: t.get('application.domain_not_valid')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkDomainsIsValidInDNS({ event, fqdn, dualCerts }): Promise<any> {
|
||||
const domain = getDomain(fqdn);
|
||||
const domainDualCert = domain.includes('www.') ? domain.replace('www.', '') : `www.${domain}`;
|
||||
dns.setServers(['1.1.1.1', '8.8.8.8']);
|
||||
let resolves = [];
|
||||
try {
|
||||
if (isIP(event.url.hostname)) {
|
||||
resolves = [event.url.hostname];
|
||||
} else {
|
||||
resolves = await dns.resolve4(event.url.hostname);
|
||||
}
|
||||
} catch (error) {
|
||||
throw {
|
||||
message: t.get('application.dns_not_set_error', { domain })
|
||||
};
|
||||
}
|
||||
|
||||
if (dualCerts) {
|
||||
try {
|
||||
const ipDomain = await dns.resolve4(domain);
|
||||
const ipDomainDualCert = await dns.resolve4(domainDualCert);
|
||||
|
||||
let ipDomainFound = false;
|
||||
let ipDomainDualCertFound = false;
|
||||
|
||||
for (const ip of ipDomain) {
|
||||
if (resolves.includes(ip)) {
|
||||
ipDomainFound = true;
|
||||
}
|
||||
}
|
||||
for (const ip of ipDomainDualCert) {
|
||||
if (resolves.includes(ip)) {
|
||||
ipDomainDualCertFound = true;
|
||||
}
|
||||
}
|
||||
if (ipDomainFound && ipDomainDualCertFound) return { status: 200 };
|
||||
throw false;
|
||||
} catch (error) {
|
||||
throw {
|
||||
message: t.get('application.dns_not_set_error', { domain })
|
||||
};
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const ipDomain = await dns.resolve4(domain);
|
||||
let ipDomainFound = false;
|
||||
for (const ip of ipDomain) {
|
||||
if (resolves.includes(ip)) {
|
||||
ipDomainFound = true;
|
||||
}
|
||||
}
|
||||
if (ipDomainFound) return { status: 200 };
|
||||
throw false;
|
||||
} catch (error) {
|
||||
throw {
|
||||
message: t.get('application.dns_not_set_error', { domain })
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
import Clickhouse from './svg/databases/Clickhouse.svelte';
|
||||
import CouchDb from './svg/databases/CouchDB.svelte';
|
||||
import MongoDb from './svg/databases/MongoDB.svelte';
|
||||
import MariaDb from './svg/databases/MariaDB.svelte';
|
||||
import MySql from './svg/databases/MySQL.svelte';
|
||||
import PostgreSql from './svg/databases/PostgreSQL.svelte';
|
||||
import Redis from './svg/databases/Redis.svelte';
|
||||
@ -17,6 +18,8 @@
|
||||
<MongoDb />
|
||||
{:else if database.type === 'mysql'}
|
||||
<MySql />
|
||||
{:else if database.type === 'mariadb'}
|
||||
<MariaDb />
|
||||
{:else if database.type === 'postgresql'}
|
||||
<PostgreSql />
|
||||
{:else if database.type === 'redis'}
|
||||
|
@ -52,6 +52,12 @@ export const supportedDatabaseTypesAndVersions = [
|
||||
versions: ['5.0', '4.4', '4.2']
|
||||
},
|
||||
{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
|
||||
{
|
||||
name: 'mariadb',
|
||||
fancyName: 'MariaDB',
|
||||
baseImage: 'bitnami/mariadb',
|
||||
versions: ['10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
|
||||
},
|
||||
{
|
||||
name: 'postgresql',
|
||||
fancyName: 'PostgreSQL',
|
||||
@ -215,3 +221,11 @@ export const supportedServiceTypesAndVersions = [
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export const getServiceMainPort = (service: string) => {
|
||||
const serviceType = supportedServiceTypesAndVersions.find((s) => s.name === service);
|
||||
if (serviceType) {
|
||||
return serviceType.ports.main;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
24
src/lib/components/svg/databases/MariaDB.svelte
Normal file
24
src/lib/components/svg/databases/MariaDB.svelte
Normal file
@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 309.88 252.72"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-12 w-12 ' : 'mx-auto w-8 h-8'}
|
||||
>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M316,10.05a4.2,4.2,0,0,0-2.84-1c-2.84,0-6.5,1.92-8.46,3l-.79.4a26.81,26.81,0,0,1-10.57,2.66c-3.76.12-7,.34-11.22.77-25,2.58-36.15,21.74-46.89,40.27-5.84,10.08-11.88,20.5-20.16,28.57a55.71,55.71,0,0,1-5.46,4.63c-8.57,6.39-19.33,10.9-27.74,14.12-8.07,3.08-16.86,5.85-25.37,8.53-7.78,2.45-15.14,4.76-21.9,7.28-3.05,1.13-5.64,2-7.93,2.76-6.15,2-10.6,3.53-17.08,8-2.53,1.73-5.07,3.6-6.8,5a71.26,71.26,0,0,0-13.54,14.27A84.81,84.81,0,0,1,77.88,163c-1.36,1.34-3.8,2-7.43,2-4.27,0-9.43-.88-14.91-1.81s-11.46-2-16.46-2c-4.07,0-7.17.66-9.5,2,0,0-3.9,2.28-5.56,5.23l1.62.73a33.56,33.56,0,0,1,6.93,5,33.68,33.68,0,0,0,7.19,5.12A6.37,6.37,0,0,1,42,180.72c-.69,1-1.69,2.29-2.74,3.67-5.77,7.55-9.13,12.32-7.2,14.92a6,6,0,0,0,3,.68c12.59,0,19.34-3.27,27.9-7.41,2.47-1.2,5-2.44,8-3.7,5-2.17,10.38-5.63,16.08-9.29,7.55-4.85,15.36-9.87,22.92-12.3a62.3,62.3,0,0,1,19.23-2.7c8,0,16.42,1.07,24.54,2.11,6.06.78,12.32,1.58,18.47,2,2.39.14,4.6.21,6.76.21a78.48,78.48,0,0,0,8.61-.45l.68-.24c4.32-2.65,6.34-8.34,8.29-13.84,1.26-3.54,2.32-6.72,4-8.74a2.06,2.06,0,0,1,.33-.27.4.4,0,0,1,.49.08.25.25,0,0,1,0,.16c-1,21.51-9.67,35.16-18.42,47.3L177,199.14s8.18,0,12.84-1.8c17-5.08,29.84-16.28,39.18-34.14a144.39,144.39,0,0,0,6.16-14.09c.16-.4,1.64-1.14,1.49.93,0,.61-.08,1.29-.13,2h0c0,.42-.06.85-.08,1.28-.25,3-1,9.34-1,9.34l5.25-2.81c12.66-8,22.42-24.14,29.82-49.25,3.09-10.46,5.34-20.85,7.33-30,2.38-11,4.43-20.43,6.78-24.09,3.69-5.74,9.32-9.62,14.77-13.39.75-.51,1.49-1,2.22-1.54,6.86-4.81,13.67-10.36,15.16-20.71l0-.23C317.93,12.92,317,11,316,10.05Z"
|
||||
transform="translate(-7.45 -9.1)"
|
||||
/>
|
||||
</svg>
|
@ -3,31 +3,13 @@
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-10 h-20 w-20' : 'mx-auto w-8 h-8'}
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 216.56 448.5"
|
||||
><defs
|
||||
><style>
|
||||
.cls-1 {
|
||||
fill: #10aa50;
|
||||
}
|
||||
.cls-2 {
|
||||
fill: #b8c4c2;
|
||||
}
|
||||
.cls-3 {
|
||||
fill: #12924f;
|
||||
}
|
||||
</style></defs
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 361.67651 499.33603"
|
||||
><path
|
||||
class="cls-1"
|
||||
d="M202.8,179.68c-23-101.47-71-128.49-83.18-147.59C113,21.7,106.25,5.91,106.25,5.91c-.66,9-1.83,14.7-9.51,21.54C81.36,41.16,16,94.42,10.51,209.72c-5.12,107.5,79,173.8,90.18,180.65,8.54,4.2,19,.08,24-3.77,40.54-27.84,96-102.07,78.06-206.92"
|
||||
/><path
|
||||
class="cls-2"
|
||||
d="M109.73,333.11c-2.11,26.62-3.63,42.11-9,57.29,0,0,3.54,25.33,6,52.17l8.77,0a488.62,488.62,0,0,1,9.57-56.2C113.71,380.8,110.16,356.46,109.73,333.11Z"
|
||||
/><path
|
||||
class="cls-3"
|
||||
d="M125.06,386.39h0c-11.48-5.3-14.8-30.13-15.31-53.28A1090.8,1090.8,0,0,0,112.2,218.4c-.6-20.07.3-185.92-4.94-210.2,2.12,4.75,7.24,15.91,12.36,23.88,12.23,19.11,60.19,46.13,83.17,147.61C220.7,284.27,165.57,358.37,125.06,386.39Z"
|
||||
/>
|
||||
</svg>
|
||||
d="M203.77731,148.85754c-10.8147-12.762-20.13269-25.8139-22.02478-28.49224a.426.426,0,0,0-.70032.00006c-1.89172,2.6784-11.20758,15.73022-22.02191,28.49218-92.69141,118.085,14.62982,197.75507,14.62982,197.75507l.87.60461c.8136,12.32624,2.83508,30.041,2.83508,30.041H185.442s2.01282-17.63849,2.83-29.96106l.87549-.68451S296.46774,266.94257,203.77731,148.85754ZM181.404,344.88123h-.001s-4.811-4.10383-6.10962-6.16l-.01172-.22131,5.81946-128.56055a.30281.30281,0,0,1,.605,0l5.81946,128.56036-.01135.22065C186.21652,340.77625,181.404,344.88123,181.404,344.88123Z"
|
||||
fill="#00684a"
|
||||
/></svg
|
||||
>
|
||||
|
@ -278,6 +278,7 @@ export async function configureApplication({
|
||||
name,
|
||||
fqdn,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@ -297,6 +298,7 @@ export async function configureApplication({
|
||||
name: string;
|
||||
fqdn: string;
|
||||
port: number;
|
||||
exposePort: number;
|
||||
installCommand: string;
|
||||
buildCommand: string;
|
||||
startCommand: string;
|
||||
@ -318,6 +320,7 @@ export async function configureApplication({
|
||||
buildPack,
|
||||
fqdn,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
|
@ -127,60 +127,73 @@ export function getServiceImages(type: string): string[] {
|
||||
|
||||
export function generateDatabaseConfiguration(database: Database & { settings: DatabaseSettings }):
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MYSQL_DATABASE: string;
|
||||
MYSQL_PASSWORD: string;
|
||||
MYSQL_ROOT_USER: string;
|
||||
MYSQL_USER: string;
|
||||
MYSQL_ROOT_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MYSQL_DATABASE: string;
|
||||
MYSQL_PASSWORD: string;
|
||||
MYSQL_ROOT_USER: string;
|
||||
MYSQL_USER: string;
|
||||
MYSQL_ROOT_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MONGODB_ROOT_USER: string;
|
||||
MONGODB_ROOT_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MONGODB_ROOT_USER: string;
|
||||
MONGODB_ROOT_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
POSTGRESQL_POSTGRES_PASSWORD: string;
|
||||
POSTGRESQL_USERNAME: string;
|
||||
POSTGRESQL_PASSWORD: string;
|
||||
POSTGRESQL_DATABASE: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MARIADB_ROOT_USER: string;
|
||||
MARIADB_ROOT_PASSWORD: string;
|
||||
MARIADB_USER: string;
|
||||
MARIADB_PASSWORD: string;
|
||||
MARIADB_DATABASE: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
REDIS_AOF_ENABLED: string;
|
||||
REDIS_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
POSTGRESQL_POSTGRES_PASSWORD: string;
|
||||
POSTGRESQL_USERNAME: string;
|
||||
POSTGRESQL_PASSWORD: string;
|
||||
POSTGRESQL_DATABASE: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
COUCHDB_PASSWORD: string;
|
||||
COUCHDB_USER: string;
|
||||
};
|
||||
} {
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
REDIS_AOF_ENABLED: string;
|
||||
REDIS_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
COUCHDB_PASSWORD: string;
|
||||
COUCHDB_USER: string;
|
||||
};
|
||||
} {
|
||||
const {
|
||||
id,
|
||||
dbUser,
|
||||
@ -207,6 +220,20 @@ export function generateDatabaseConfiguration(database: Database & { settings: D
|
||||
volume: `${id}-${type}-data:/bitnami/mysql/data`,
|
||||
ulimits: {}
|
||||
};
|
||||
} else if (type === 'mariadb') {
|
||||
return {
|
||||
privatePort: 3306,
|
||||
environmentVariables: {
|
||||
MARIADB_ROOT_USER: rootUser,
|
||||
MARIADB_ROOT_PASSWORD: rootUserPassword,
|
||||
MARIADB_USER: dbUser,
|
||||
MARIADB_PASSWORD: dbUserPassword,
|
||||
MARIADB_DATABASE: defaultDatabase
|
||||
},
|
||||
image: `${baseImage}:${version}`,
|
||||
volume: `${id}-${type}-data:/bitnami/mariadb`,
|
||||
ulimits: {}
|
||||
};
|
||||
} else if (type === 'mongodb') {
|
||||
return {
|
||||
privatePort: 27017,
|
||||
|
@ -184,6 +184,10 @@ export async function updatePasswordInDb(database, user, newPassword, isRoot) {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker exec ${id} mysql -u ${rootUser} -p${rootUserPassword} -e \"ALTER USER '${user}'@'%' IDENTIFIED WITH caching_sha2_password BY '${newPassword}';\"`
|
||||
);
|
||||
} else if (type === 'mariadb') {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker exec ${id} mysql -u ${rootUser} -p${rootUserPassword} -e \"SET PASSWORD FOR '${user}'@'%' = PASSWORD('${newPassword}');\"`
|
||||
);
|
||||
} else if (type === 'postgresql') {
|
||||
if (isRoot) {
|
||||
await asyncExecShell(
|
||||
|
@ -327,35 +327,40 @@ export async function updatePlausibleAnalyticsService({
|
||||
id,
|
||||
fqdn,
|
||||
email,
|
||||
exposePort,
|
||||
username,
|
||||
name
|
||||
}: {
|
||||
id: string;
|
||||
fqdn: string;
|
||||
exposePort?: number;
|
||||
name: string;
|
||||
email: string;
|
||||
username: string;
|
||||
}): Promise<void> {
|
||||
await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } });
|
||||
await prisma.service.update({ where: { id }, data: { name, fqdn } });
|
||||
await prisma.service.update({ where: { id }, data: { name, fqdn, exposePort } });
|
||||
}
|
||||
|
||||
export async function updateService({
|
||||
id,
|
||||
fqdn,
|
||||
exposePort,
|
||||
name
|
||||
}: {
|
||||
id: string;
|
||||
fqdn: string;
|
||||
exposePort?: number;
|
||||
name: string;
|
||||
}): Promise<Service> {
|
||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||
return await prisma.service.update({ where: { id }, data: { fqdn, name, exposePort } });
|
||||
}
|
||||
|
||||
export async function updateFiderService({
|
||||
id,
|
||||
fqdn,
|
||||
name,
|
||||
exposePort,
|
||||
emailNoreply,
|
||||
emailMailgunApiKey,
|
||||
emailMailgunDomain,
|
||||
@ -368,6 +373,7 @@ export async function updateFiderService({
|
||||
}: {
|
||||
id: string;
|
||||
fqdn: string;
|
||||
exposePort?: number;
|
||||
name: string;
|
||||
emailNoreply: string;
|
||||
emailMailgunApiKey: string;
|
||||
@ -384,6 +390,7 @@ export async function updateFiderService({
|
||||
data: {
|
||||
fqdn,
|
||||
name,
|
||||
exposePort,
|
||||
fider: {
|
||||
update: {
|
||||
emailNoreply,
|
||||
@ -405,18 +412,20 @@ export async function updateWordpress({
|
||||
id,
|
||||
fqdn,
|
||||
name,
|
||||
exposePort,
|
||||
mysqlDatabase,
|
||||
extraConfig
|
||||
}: {
|
||||
id: string;
|
||||
fqdn: string;
|
||||
name: string;
|
||||
exposePort?: number;
|
||||
mysqlDatabase: string;
|
||||
extraConfig: string;
|
||||
}): Promise<Service> {
|
||||
return await prisma.service.update({
|
||||
where: { id },
|
||||
data: { fqdn, name, wordpress: { update: { mysqlDatabase, extraConfig } } }
|
||||
data: { fqdn, name, exposePort, wordpress: { update: { mysqlDatabase, extraConfig } } }
|
||||
});
|
||||
}
|
||||
|
||||
@ -434,16 +443,18 @@ export async function updateGhostService({
|
||||
id,
|
||||
fqdn,
|
||||
name,
|
||||
exposePort,
|
||||
mariadbDatabase
|
||||
}: {
|
||||
id: string;
|
||||
fqdn: string;
|
||||
name: string;
|
||||
exposePort?: number;
|
||||
mariadbDatabase: string;
|
||||
}): Promise<Service> {
|
||||
return await prisma.service.update({
|
||||
where: { id },
|
||||
data: { fqdn, name, ghost: { update: { mariadbDatabase } } }
|
||||
data: { fqdn, name, exposePort, ghost: { update: { mariadbDatabase } } }
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -292,26 +292,28 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
}
|
||||
|
||||
export async function renewSSLCerts(): Promise<void> {
|
||||
const host = 'unix:///var/run/docker.sock';
|
||||
await asyncExecShell(`docker pull alpine:latest`);
|
||||
const certbotImage =
|
||||
process.arch === 'x64' ? 'certbot/certbot' : 'certbot/certbot:arm64v8-latest';
|
||||
if (!dev) {
|
||||
const host = 'unix:///var/run/docker.sock';
|
||||
await asyncExecShell(`docker pull alpine:latest`);
|
||||
const certbotImage =
|
||||
process.arch === 'x64' ? 'certbot/certbot' : 'certbot/certbot:arm64v8-latest';
|
||||
|
||||
const { stdout: certificates } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "ls -1 /etc/letsencrypt/live/ | grep -v README"`
|
||||
);
|
||||
const { stdout: certificates } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "ls -1 /etc/letsencrypt/live/ | grep -v README"`
|
||||
);
|
||||
|
||||
for (const certificate of certificates.trim().split('\n')) {
|
||||
try {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm --name certbot-renewal -p 9080:9080 -v "coolify-letsencrypt:/etc/letsencrypt" ${certbotImage} --cert-name ${certificate} --logs-dir /etc/letsencrypt/logs renew --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port 9080`
|
||||
);
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "test -d /etc/letsencrypt/live/${certificate}/ && cat /etc/letsencrypt/live/${certificate}/fullchain.pem /etc/letsencrypt/live/${certificate}/privkey.pem > /app/ssl/${certificate}.pem"`
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
for (const certificate of certificates.trim().split('\n')) {
|
||||
try {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm --name certbot-renewal -p 9080:9080 -v "coolify-letsencrypt:/etc/letsencrypt" ${certbotImage} --cert-name ${certificate} --logs-dir /etc/letsencrypt/logs renew --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port 9080`
|
||||
);
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "test -d /etc/letsencrypt/live/${certificate}/ && cat /etc/letsencrypt/live/${certificate}/fullchain.pem /etc/letsencrypt/live/${certificate}/privkey.pem > /app/ssl/${certificate}.pem"`
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
await reloadHaproxy('unix:///var/run/docker.sock');
|
||||
}
|
||||
await reloadHaproxy('unix:///var/run/docker.sock');
|
||||
}
|
||||
|
@ -178,9 +178,11 @@
|
||||
"delete_application": "Delete application",
|
||||
"permission_denied_delete_application": "You do not have permission to delete this application",
|
||||
"domain_already_in_use": "Domain {{domain}} is already used.",
|
||||
"dns_not_set_error": "DNS not set or propogated for {{domain}}.<br><br>Please check your DNS settings.",
|
||||
"dns_not_set_error": "DNS not set correctly or propogated for {{domain}}.<br><br>Please check your DNS settings.",
|
||||
"domain_required": "Domain is required.",
|
||||
"settings_saved": "Settings saved.",
|
||||
"dns_not_set_partial_error": "DNS not set",
|
||||
"domain_not_valid": "Could not resolve domain or it's not pointing to the server IP address.<br><br>Please check your DNS configuration and try again.",
|
||||
"git_source": "Git Source",
|
||||
"git_repository": "Git Repository",
|
||||
"build_pack": "Build Pack",
|
||||
@ -204,6 +206,7 @@
|
||||
"enable_automatic_deployment": "Enable Automatic Deployment",
|
||||
"enable_auto_deploy_webhooks": "Enable automatic deployment through webhooks.",
|
||||
"enable_mr_pr_previews": "Enable MR/PR Previews",
|
||||
"expose_a_port": "Expose a port",
|
||||
"enable_preview_deploy_mr_pr_requests": "Enable preview deployments from pull or merge requests.",
|
||||
"debug_logs": "Debug Logs",
|
||||
"enable_debug_log_during_build": "Enable debug logs during build phase.<br><span class='text-red-500 font-bold'>Sensitive information</span> could be visible and saved in logs.",
|
||||
@ -311,7 +314,7 @@
|
||||
"credential_stat_explainer": "Credentials for <a class=\"text-white font-bold\" href=\"{{link}}\" target=\"_blank\">stats</a> page.",
|
||||
"auto_update_enabled": "Auto update enabled?",
|
||||
"auto_update_enabled_explainer": "Enable automatic updates for Coolify. It will be done automatically behind the scenes, if there is no build process running.",
|
||||
"generate_www_non_www_ssl": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-yellow-500'>both DNS entries</span> set in advance.<br><br>Service needs to be restarted.",
|
||||
"generate_www_non_www_ssl": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-yellow-500'>both DNS entries</span> set in advance.",
|
||||
"is_dns_check_enabled": "DNS check enabled?",
|
||||
"is_dns_check_enabled_explainer": "You can disable DNS check before creating SSL certificates.<br><br>Turning it off is useful when Coolify is behind a reverse proxy or tunnel."
|
||||
},
|
||||
|
@ -61,6 +61,7 @@
|
||||
"enable_debug_log_during_build": "Activez les journaux de débogage pendant la phase de build.<br><span class='text-red-500 font-bold'>Les informations sensibles</span> peuvent être visibles et enregistrées dans les journaux.",
|
||||
"enable_mr_pr_previews": "Activer les aperçus MR/PR",
|
||||
"enable_preview_deploy_mr_pr_requests": "Activez les déploiements de prévisualisation à partir de demandes d'extraction ou de fusion.",
|
||||
"expose_a_port": "Exposer un port",
|
||||
"features": "Caractéristiques",
|
||||
"git_repository": "Dépôt Git",
|
||||
"git_source": "Source Git",
|
||||
|
@ -48,6 +48,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
pythonModule,
|
||||
pythonVariable,
|
||||
denoOptions,
|
||||
exposePort,
|
||||
baseImage,
|
||||
baseBuildImage
|
||||
} = job.data;
|
||||
@ -152,6 +153,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
JSON.stringify({
|
||||
buildPack,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@ -207,7 +209,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
tag,
|
||||
workdir,
|
||||
docker,
|
||||
port,
|
||||
port: exposePort ? `${exposePort}:${port}` : port,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@ -263,7 +265,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
repository,
|
||||
branch,
|
||||
projectId,
|
||||
port,
|
||||
port: exposePort ? `${exposePort}:${port}` : port,
|
||||
commit,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
@ -298,6 +300,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
labels,
|
||||
depends_on: [],
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
// logging: {
|
||||
// driver: 'fluentd',
|
||||
// },
|
||||
|
@ -41,7 +41,6 @@ export default async function (): Promise<void> {
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
console.log(`Is LowDiskSpace detected? ${lowDiskSpace}`);
|
||||
if (lowDiskSpace) {
|
||||
// Cleanup old coolify images
|
||||
try {
|
||||
|
@ -117,7 +117,7 @@ const cron = async (): Promise<void> => {
|
||||
await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } });
|
||||
if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } });
|
||||
if (!dev) await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
|
||||
await queue.autoUpdater.add('autoUpdater', {}, { repeat: { every: 60000 } });
|
||||
if (!dev) await queue.autoUpdater.add('autoUpdater', {}, { repeat: { every: 60000 } });
|
||||
};
|
||||
cron().catch((error) => {
|
||||
console.log('cron failed to start');
|
||||
|
@ -12,6 +12,7 @@ export type BuilderJob = {
|
||||
buildPack: BuildPackName;
|
||||
projectId: number;
|
||||
port: number;
|
||||
exposePort?: number;
|
||||
installCommand: string;
|
||||
buildCommand?: string;
|
||||
startCommand?: string;
|
||||
|
@ -18,6 +18,7 @@ export type ComposeFileService = {
|
||||
restart: ComposeFileRestartOption;
|
||||
depends_on?: string[];
|
||||
command?: string;
|
||||
ports?: string[];
|
||||
build?: {
|
||||
context: string;
|
||||
dockerfile: string;
|
||||
|
@ -17,21 +17,25 @@ export const post: RequestHandler = async (event) => {
|
||||
let count = 0;
|
||||
await new Promise<void>(async (resolve, reject) => {
|
||||
const job = await buildQueue.getJob(buildId);
|
||||
if (!job) {
|
||||
return resolve();
|
||||
}
|
||||
const {
|
||||
destinationDocker: { engine }
|
||||
} = job.data;
|
||||
} = job?.data;
|
||||
const host = getEngine(engine);
|
||||
let interval = setInterval(async () => {
|
||||
const { status } = await db.prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (status === 'failed') {
|
||||
clearInterval(interval);
|
||||
return resolve();
|
||||
}
|
||||
if (count > 1200) {
|
||||
clearInterval(interval);
|
||||
reject(new Error('Could not cancel build.'));
|
||||
}
|
||||
try {
|
||||
const data = await db.prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (data?.status === 'failed') {
|
||||
clearInterval(interval);
|
||||
return resolve();
|
||||
}
|
||||
if (count > 60) {
|
||||
clearInterval(interval);
|
||||
reject(new Error('Could not cancel build.'));
|
||||
}
|
||||
|
||||
const { stdout: buildContainers } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker container ls --filter "label=coolify.buildId=${buildId}" --format '{{json .}}'`
|
||||
);
|
||||
@ -53,11 +57,14 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
count++;
|
||||
} catch (error) {}
|
||||
}, 100);
|
||||
}, 1000);
|
||||
|
||||
resolve();
|
||||
});
|
||||
|
||||
const data = await db.prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (data?.status === 'queued' || data?.status === 'running') {
|
||||
await db.prisma.build.update({ where: { id: buildId }, data: { status: 'failed' } });
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
|
@ -1,21 +1,44 @@
|
||||
import { dev } from '$app/env';
|
||||
import { getDomain, getUserDetails } from '$lib/common';
|
||||
import { checkDomainsIsValidInDNS, getDomain, getUserDetails, isDNSValid } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { promises as dns } from 'dns';
|
||||
import getPort from 'get-port';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const { status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
const domain = event.url.searchParams.get('domain');
|
||||
if (!domain) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
message: t.get('application.domain_required')
|
||||
}
|
||||
};
|
||||
}
|
||||
try {
|
||||
await isDNSValid(event, domain);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = event.params;
|
||||
let { fqdn, forceSave } = await event.request.json();
|
||||
let { exposePort, fqdn, forceSave, dualCerts } = await event.request.json();
|
||||
fqdn = fqdn.toLowerCase();
|
||||
|
||||
try {
|
||||
const domain = getDomain(fqdn);
|
||||
const { isDNSCheckEnabled } = await db.prisma.setting.findFirst({});
|
||||
const found = await db.isDomainConfigured({ id, fqdn });
|
||||
if (found) {
|
||||
throw {
|
||||
@ -24,25 +47,22 @@ export const post: RequestHandler = async (event) => {
|
||||
})
|
||||
};
|
||||
}
|
||||
if (!dev && !forceSave) {
|
||||
let ip = [];
|
||||
let localIp = [];
|
||||
dns.setServers(['1.1.1.1', '8.8.8.8']);
|
||||
|
||||
try {
|
||||
localIp = await dns.resolve4(event.url.hostname);
|
||||
} catch (error) {}
|
||||
try {
|
||||
ip = await dns.resolve4(domain);
|
||||
} catch (error) {}
|
||||
if (exposePort) {
|
||||
exposePort = Number(exposePort);
|
||||
|
||||
if (localIp?.length > 0) {
|
||||
if (ip?.length === 0 || !ip.includes(localIp[0])) {
|
||||
throw {
|
||||
message: t.get('application.dns_not_set_error', { domain: domain })
|
||||
};
|
||||
}
|
||||
if (exposePort < 1024 || exposePort > 65535) {
|
||||
throw { message: `Expose Port needs to be between 1024 and 65535.` };
|
||||
}
|
||||
|
||||
const publicPort = await getPort({ port: exposePort });
|
||||
if (publicPort !== exposePort) {
|
||||
throw { message: `Port ${exposePort} is already in use.` };
|
||||
}
|
||||
}
|
||||
|
||||
if (isDNSCheckEnabled && !dev && !forceSave) {
|
||||
return await checkDomainsIsValidInDNS({ event, fqdn, dualCerts });
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -22,6 +22,7 @@ export const post: RequestHandler = async (event) => {
|
||||
JSON.stringify({
|
||||
buildPack: applicationFound.buildPack,
|
||||
port: applicationFound.port,
|
||||
exposePort: applicationFound.exposePort,
|
||||
installCommand: applicationFound.installCommand,
|
||||
buildCommand: applicationFound.buildCommand,
|
||||
startCommand: applicationFound.startCommand
|
||||
|
@ -3,8 +3,6 @@ import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { checkContainer, isContainerExited } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
import { get as getRequest } from '$lib/api';
|
||||
import { setDefaultConfiguration } from '$lib/buildPacks/common';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@ -52,6 +50,7 @@ export const post: RequestHandler = async (event) => {
|
||||
buildPack,
|
||||
fqdn,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@ -67,6 +66,9 @@ export const post: RequestHandler = async (event) => {
|
||||
baseBuildImage
|
||||
} = await event.request.json();
|
||||
if (port) port = Number(port);
|
||||
if (exposePort) {
|
||||
exposePort = Number(exposePort);
|
||||
}
|
||||
if (denoOptions) denoOptions = denoOptions.trim();
|
||||
|
||||
try {
|
||||
@ -87,6 +89,7 @@ export const post: RequestHandler = async (event) => {
|
||||
name,
|
||||
fqdn,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
|
@ -45,9 +45,9 @@
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import type Prisma from '@prisma/client';
|
||||
import { notNodeDeployments, staticDeployments } from '$lib/components/common';
|
||||
import { getDomain, notNodeDeployments, staticDeployments } from '$lib/components/common';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { post } from '$lib/api';
|
||||
import { get, post } from '$lib/api';
|
||||
import cuid from 'cuid';
|
||||
import { browser } from '$app/env';
|
||||
import { disabledButton } from '$lib/store';
|
||||
@ -63,6 +63,10 @@
|
||||
let dualCerts = application.settings.dualCerts;
|
||||
let autodeploy = application.settings.autodeploy;
|
||||
|
||||
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||
let isNonWWWDomainOK = false;
|
||||
let isWWWDomainOK = false;
|
||||
|
||||
let wsgis = [
|
||||
{
|
||||
value: 'None',
|
||||
@ -127,13 +131,31 @@
|
||||
async function handleSubmit() {
|
||||
loading = true;
|
||||
try {
|
||||
await post(`/applications/${id}/check.json`, { fqdn: application.fqdn, forceSave });
|
||||
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||
await post(`/applications/${id}/check.json`, {
|
||||
fqdn: application.fqdn,
|
||||
forceSave,
|
||||
dualCerts,
|
||||
exposePort: application.exposePort
|
||||
});
|
||||
await post(`/applications/${id}.json`, { ...application });
|
||||
$disabledButton = false;
|
||||
forceSave = false;
|
||||
return toast.push('Configurations saved.');
|
||||
} catch ({ error }) {
|
||||
if (error?.startsWith($t('application.dns_not_set_partial_error'))) {
|
||||
forceSave = true;
|
||||
if (dualCerts) {
|
||||
isNonWWWDomainOK = await isDNSValid(getDomain(nonWWWDomain), false);
|
||||
isWWWDomainOK = await isDNSValid(getDomain(`www.${nonWWWDomain}`), true);
|
||||
} else {
|
||||
const isWWW = getDomain(application.fqdn).includes('www.');
|
||||
if (isWWW) {
|
||||
isWWWDomainOK = await isDNSValid(getDomain(`www.${nonWWWDomain}`), true);
|
||||
} else {
|
||||
isNonWWWDomainOK = await isDNSValid(getDomain(nonWWWDomain), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
@ -151,6 +173,19 @@
|
||||
application.baseBuildImage = event.detail.value;
|
||||
await handleSubmit();
|
||||
}
|
||||
|
||||
async function isDNSValid(domain, isWWW) {
|
||||
try {
|
||||
await get(`/applications/${id}/check.json?domain=${domain}`);
|
||||
toast.push('DNS configuration is valid.');
|
||||
isWWW ? (isWWWDomainOK = true) : (isNonWWWDomainOK = true);
|
||||
return true;
|
||||
} catch ({ error }) {
|
||||
errorNotification(error);
|
||||
isWWW ? (isWWWDomainOK = false) : (isNonWWWDomainOK = false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex items-center space-x-2 p-5 px-6 font-bold">
|
||||
@ -383,17 +418,52 @@
|
||||
{/if}
|
||||
<Explainer text={$t('application.https_explainer')} />
|
||||
</div>
|
||||
<input
|
||||
readonly={!$session.isAdmin || isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
bind:this={domainEl}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
bind:value={application.fqdn}
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
placeholder="eg: https://coollabs.io"
|
||||
required
|
||||
/>
|
||||
<div>
|
||||
<input
|
||||
readonly={!$session.isAdmin || isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
bind:this={domainEl}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
bind:value={application.fqdn}
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
placeholder="eg: https://coollabs.io"
|
||||
/>
|
||||
{#if forceSave}
|
||||
<div class="flex-col space-y-2 pt-4 text-center">
|
||||
{#if isNonWWWDomainOK}
|
||||
<button
|
||||
class="bg-green-600 hover:bg-green-500"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="bg-red-600 hover:bg-red-500"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
|
||||
>
|
||||
{/if}
|
||||
{#if dualCerts}
|
||||
{#if isWWWDomainOK}
|
||||
<button
|
||||
class="bg-green-600 hover:bg-green-500"
|
||||
on:click|preventDefault={() =>
|
||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="bg-red-600 hover:bg-red-500"
|
||||
on:click|preventDefault={() =>
|
||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center pb-8">
|
||||
<Setting
|
||||
@ -451,9 +521,23 @@
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !notNodeDeployments.includes(application.buildPack)}
|
||||
{#if application.buildPack !== 'docker'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="exposePort"
|
||||
id="exposePort"
|
||||
bind:value={application.exposePort}
|
||||
placeholder="12345"
|
||||
/>
|
||||
<Explainer
|
||||
text={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !notNodeDeployments.includes(application.buildPack)}
|
||||
<div class="grid grid-cols-2 items-center pt-4">
|
||||
<label for="installCommand" class="text-base font-bold text-stone-100"
|
||||
>{$t('application.install_command')}</label
|
||||
>
|
||||
@ -491,7 +575,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if application.buildPack === 'docker'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div class="grid grid-cols-2 items-center pt-4">
|
||||
<label for="dockerFileLocation" class="text-base font-bold text-stone-100"
|
||||
>Dockerfile Location</label
|
||||
>
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
import MySql from './_MySQL.svelte';
|
||||
import MongoDb from './_MongoDB.svelte';
|
||||
import MariaDb from './_MariaDB.svelte';
|
||||
import PostgreSql from './_PostgreSQL.svelte';
|
||||
import Redis from './_Redis.svelte';
|
||||
import CouchDb from './_CouchDb.svelte';
|
||||
@ -190,6 +191,8 @@
|
||||
<PostgreSql bind:database {isRunning} />
|
||||
{:else if database.type === 'mongodb'}
|
||||
<MongoDb bind:database {isRunning} />
|
||||
{:else if database.type === 'mariadb'}
|
||||
<MariaDb bind:database {isRunning} />
|
||||
{:else if database.type === 'redis'}
|
||||
<Redis bind:database {isRunning} />
|
||||
{:else if database.type === 'couchdb'}
|
||||
|
79
src/routes/databases/[id]/_Databases/_MariaDB.svelte
Normal file
79
src/routes/databases/[id]/_Databases/_MariaDB.svelte
Normal file
@ -0,0 +1,79 @@
|
||||
<script>
|
||||
export let database;
|
||||
export let isRunning;
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import { t } from '$lib/translations';
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 py-5 font-bold">
|
||||
<div class="title">MariaDB</div>
|
||||
</div>
|
||||
<div class="space-y-2 px-10">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="defaultDatabase" class="text-base font-bold text-stone-100"
|
||||
>{$t('database.default_database')}</label
|
||||
>
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
disabled={database.defaultDatabase}
|
||||
placeholder="{$t('forms.eg')}: mydb"
|
||||
id="defaultDatabase"
|
||||
name="defaultDatabase"
|
||||
bind:value={database.defaultDatabase}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUser" class="text-base font-bold text-stone-100">{$t('forms.user')}</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder={$t('forms.generated_automatically_after_start')}
|
||||
id="dbUser"
|
||||
name="dbUser"
|
||||
value={database.dbUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUserPassword" class="text-base font-bold text-stone-100"
|
||||
>{$t('forms.password')}</label
|
||||
>
|
||||
<CopyPasswordField
|
||||
disabled={!isRunning}
|
||||
readonly={!isRunning}
|
||||
placeholder={$t('forms.generated_automatically_after_start')}
|
||||
isPasswordField
|
||||
id="dbUserPassword"
|
||||
name="dbUserPassword"
|
||||
bind:value={database.dbUserPassword}
|
||||
/>
|
||||
<Explainer text="Could be changed while the database is running." />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUser" class="text-base font-bold text-stone-100">{$t('forms.root_user')}</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder={$t('forms.generated_automatically_after_start')}
|
||||
id="rootUser"
|
||||
name="rootUser"
|
||||
value={database.rootUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUserPassword" class="text-base font-bold text-stone-100"
|
||||
>{$t('forms.roots_password')}</label
|
||||
>
|
||||
<CopyPasswordField
|
||||
disabled={!isRunning}
|
||||
readonly={!isRunning}
|
||||
placeholder={$t('forms.generated_automatically_after_start')}
|
||||
isPasswordField
|
||||
id="rootUserPassword"
|
||||
name="rootUserPassword"
|
||||
bind:value={database.rootUserPassword}
|
||||
/>
|
||||
<Explainer text="Could be changed while the database is running." />
|
||||
</div>
|
||||
</div>
|
@ -37,6 +37,7 @@
|
||||
import Clickhouse from '$lib/components/svg/databases/Clickhouse.svelte';
|
||||
import CouchDB from '$lib/components/svg/databases/CouchDB.svelte';
|
||||
import MongoDB from '$lib/components/svg/databases/MongoDB.svelte';
|
||||
import MariaDB from '$lib/components/svg/databases/MariaDB.svelte';
|
||||
import MySQL from '$lib/components/svg/databases/MySQL.svelte';
|
||||
import PostgreSQL from '$lib/components/svg/databases/PostgreSQL.svelte';
|
||||
import Redis from '$lib/components/svg/databases/Redis.svelte';
|
||||
@ -68,6 +69,8 @@
|
||||
<CouchDB isAbsolute />
|
||||
{:else if type.name === 'mongodb'}
|
||||
<MongoDB isAbsolute />
|
||||
{:else if type.name === 'mariadb'}
|
||||
<MariaDB isAbsolute />
|
||||
{:else if type.name === 'mysql'}
|
||||
<MySQL isAbsolute />
|
||||
{:else if type.name === 'postgresql'}
|
||||
|
@ -3,6 +3,7 @@
|
||||
import Clickhouse from '$lib/components/svg/databases/Clickhouse.svelte';
|
||||
import CouchDB from '$lib/components/svg/databases/CouchDB.svelte';
|
||||
import MongoDB from '$lib/components/svg/databases/MongoDB.svelte';
|
||||
import MariaDB from '$lib/components/svg/databases/MariaDB.svelte';
|
||||
import MySQL from '$lib/components/svg/databases/MySQL.svelte';
|
||||
import PostgreSQL from '$lib/components/svg/databases/PostgreSQL.svelte';
|
||||
import Redis from '$lib/components/svg/databases/Redis.svelte';
|
||||
@ -66,6 +67,8 @@
|
||||
<MongoDB isAbsolute />
|
||||
{:else if database.type === 'mysql'}
|
||||
<MySQL isAbsolute />
|
||||
{:else if database.type === 'mariadb'}
|
||||
<MariaDB isAbsolute />
|
||||
{:else if database.type === 'postgresql'}
|
||||
<PostgreSQL isAbsolute />
|
||||
{:else if database.type === 'redis'}
|
||||
@ -98,6 +101,8 @@
|
||||
<CouchDB isAbsolute />
|
||||
{:else if database.type === 'mongodb'}
|
||||
<MongoDB isAbsolute />
|
||||
{:else if database.type === 'mariadb'}
|
||||
<MariaDB isAbsolute />
|
||||
{:else if database.type === 'mysql'}
|
||||
<MySQL isAbsolute />
|
||||
{:else if database.type === 'postgresql'}
|
||||
|
@ -27,6 +27,7 @@
|
||||
let loading = false;
|
||||
let loadingVerification = false;
|
||||
let dualCerts = service.dualCerts;
|
||||
let showExposePort = service.exposePort !== null;
|
||||
|
||||
async function handleSubmit() {
|
||||
loading = true;
|
||||
@ -160,6 +161,32 @@
|
||||
on:click={() => !isRunning && changeSettings('dualCerts')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
isCenter={false}
|
||||
bind:setting={showExposePort}
|
||||
on:click={() => {
|
||||
showExposePort = !showExposePort;
|
||||
service.exposePort = undefined;
|
||||
}}
|
||||
title={$t('application.expose_a_port')}
|
||||
description="Expose a port to the host system"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if showExposePort}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="exposePort" class="text-base font-bold text-stone-100">Expose Port</label>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="exposePort"
|
||||
id="exposePort"
|
||||
bind:value={service.exposePort}
|
||||
placeholder="12345"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if service.type === 'plausibleanalytics'}
|
||||
<PlausibleAnalytics bind:service {readOnly} />
|
||||
{:else if service.type === 'minio'}
|
||||
|
@ -11,11 +11,12 @@ export const post: RequestHandler = async (event) => {
|
||||
let {
|
||||
name,
|
||||
fqdn,
|
||||
exposePort,
|
||||
ghost: { mariadbDatabase }
|
||||
} = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
try {
|
||||
await db.updateGhostService({ id, fqdn, name, mariadbDatabase });
|
||||
await db.updateGhostService({ id, fqdn, name, exposePort, mariadbDatabase });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -12,6 +12,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -19,6 +20,8 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
const port = getServiceMainPort('ghost');
|
||||
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const {
|
||||
@ -27,6 +30,7 @@ export const post: RequestHandler = async (event) => {
|
||||
destinationDockerId,
|
||||
destinationDocker,
|
||||
serviceSecret,
|
||||
exposePort,
|
||||
fqdn,
|
||||
ghost: {
|
||||
defaultEmail,
|
||||
@ -89,6 +93,7 @@ export const post: RequestHandler = async (event) => {
|
||||
volumes: [config.ghost.volume],
|
||||
environment: config.ghost.environmentVariables,
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
labels: makeLabelForServices('ghost'),
|
||||
depends_on: [`${id}-mariadb`],
|
||||
deploy: {
|
||||
|
@ -9,13 +9,15 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -15,9 +16,11 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret } = service;
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort } =
|
||||
service;
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('languagetool');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
@ -42,6 +45,7 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
volumes: [config.volume],
|
||||
labels: makeLabelForServices('languagetool'),
|
||||
deploy: {
|
||||
|
@ -9,11 +9,12 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -18,9 +19,11 @@ export const post: RequestHandler = async (event) => {
|
||||
const {
|
||||
meiliSearch: { masterKey }
|
||||
} = service;
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret } = service;
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort } =
|
||||
service;
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('meilisearch');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
@ -47,6 +50,7 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
volumes: [config.volume],
|
||||
labels: makeLabelForServices('meilisearch'),
|
||||
deploy: {
|
||||
|
@ -9,11 +9,12 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -7,6 +7,7 @@ import { startHttpProxy } from '$lib/haproxy';
|
||||
import { ErrorHandler, getFreePort, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -22,12 +23,14 @@ export const post: RequestHandler = async (event) => {
|
||||
fqdn,
|
||||
destinationDockerId,
|
||||
destinationDocker,
|
||||
exposePort,
|
||||
minio: { rootUser, rootUserPassword },
|
||||
serviceSecret
|
||||
} = service;
|
||||
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('minio');
|
||||
|
||||
const publicPort = await getFreePort();
|
||||
|
||||
@ -62,6 +65,7 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
volumes: [config.volume],
|
||||
restart: 'always',
|
||||
...(exposePort && { ports: [`${port}:${port}`] }),
|
||||
labels: makeLabelForServices('minio'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
|
@ -8,11 +8,12 @@ export const post: RequestHandler = async (event) => {
|
||||
if (status === 401) return { status, body };
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -15,9 +16,11 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret } = service;
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort } =
|
||||
service;
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('n8n');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
|
@ -8,11 +8,12 @@ export const post: RequestHandler = async (event) => {
|
||||
if (status === 401) return { status, body };
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -15,9 +16,11 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret } = service;
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort } =
|
||||
service;
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('nocodb');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
@ -40,6 +43,7 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
labels: makeLabelForServices('nocodb'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
|
@ -11,14 +11,16 @@ export const post: RequestHandler = async (event) => {
|
||||
let {
|
||||
name,
|
||||
fqdn,
|
||||
exposePort,
|
||||
plausibleAnalytics: { email, username }
|
||||
} = await event.request.json();
|
||||
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (email) email = email.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updatePlausibleAnalyticsService({ id, fqdn, name, email, username });
|
||||
await db.updatePlausibleAnalyticsService({ id, fqdn, name, email, username, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -22,6 +23,7 @@ export const post: RequestHandler = async (event) => {
|
||||
destinationDockerId,
|
||||
destinationDocker,
|
||||
serviceSecret,
|
||||
exposePort,
|
||||
plausibleAnalytics: {
|
||||
id: plausibleDbId,
|
||||
username,
|
||||
@ -78,6 +80,7 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('plausibleanalytics');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
|
||||
@ -132,6 +135,7 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
||||
networks: [network],
|
||||
environment: config.plausibleAnalytics.environmentVariables,
|
||||
restart: 'always',
|
||||
...(exposePort && { ports: [`${port}:${exposePort}`] }),
|
||||
depends_on: [`${id}-postgresql`, `${id}-clickhouse`],
|
||||
labels: makeLabelForServices('plausibleAnalytics'),
|
||||
deploy: {
|
||||
|
@ -9,11 +9,12 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -8,6 +8,7 @@ import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import type { Service, DestinationDocker, Prisma } from '@prisma/client';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -24,6 +25,7 @@ export const post: RequestHandler = async (event) => {
|
||||
destinationDockerId,
|
||||
destinationDocker,
|
||||
serviceSecret,
|
||||
exposePort,
|
||||
umami: {
|
||||
umamiAdminPassword,
|
||||
postgresqlUser,
|
||||
@ -34,6 +36,7 @@ export const post: RequestHandler = async (event) => {
|
||||
} = service;
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('umami');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
@ -156,6 +159,7 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
volumes: [],
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${port}:${port}`] } : {}),
|
||||
labels: makeLabelForServices('umami'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
|
@ -8,11 +8,12 @@ export const post: RequestHandler = async (event) => {
|
||||
if (status === 401) return { status, body };
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -15,9 +16,11 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret } = service;
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort } =
|
||||
service;
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('uptimekuma');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
@ -42,6 +45,7 @@ export const post: RequestHandler = async (event) => {
|
||||
volumes: [config.volume],
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
labels: makeLabelForServices('uptimekuma'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
|
@ -8,11 +8,12 @@ export const post: RequestHandler = async (event) => {
|
||||
if (status === 401) return { status, body };
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { getServiceImage, ErrorHandler } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -15,10 +16,12 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret } = service;
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort } =
|
||||
service;
|
||||
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('vaultwarden');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
@ -43,6 +46,7 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
volumes: [config.volume],
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
labels: makeLabelForServices('vaultWarden'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
|
@ -9,11 +9,12 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -22,11 +23,13 @@ export const post: RequestHandler = async (event) => {
|
||||
destinationDocker,
|
||||
serviceSecret,
|
||||
persistentStorage,
|
||||
exposePort,
|
||||
vscodeserver: { password }
|
||||
} = service;
|
||||
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('vscodeserver');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
@ -75,6 +78,7 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
volumes: [config.volume, ...volumes],
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${port}:${exposePort}`] } : {}),
|
||||
labels: makeLabelForServices('vscodeServer'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
|
@ -11,12 +11,14 @@ export const post: RequestHandler = async (event) => {
|
||||
let {
|
||||
name,
|
||||
fqdn,
|
||||
exposePort,
|
||||
wordpress: { extraConfig, mysqlDatabase }
|
||||
} = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateWordpress({ id, fqdn, name, extraConfig, mysqlDatabase });
|
||||
await db.updateWordpress({ id, fqdn, name, extraConfig, mysqlDatabase, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -8,7 +8,6 @@ import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import cuid from 'cuid';
|
||||
import fs from 'fs/promises';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
|
@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@ -22,6 +23,7 @@ export const post: RequestHandler = async (event) => {
|
||||
destinationDockerId,
|
||||
serviceSecret,
|
||||
destinationDocker,
|
||||
exposePort,
|
||||
wordpress: {
|
||||
mysqlDatabase,
|
||||
mysqlUser,
|
||||
@ -35,6 +37,7 @@ export const post: RequestHandler = async (event) => {
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const image = getServiceImage(type);
|
||||
const port = getServiceMainPort('wordpress');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const config = {
|
||||
@ -76,6 +79,7 @@ export const post: RequestHandler = async (event) => {
|
||||
volumes: [config.wordpress.volume],
|
||||
networks: [network],
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${port}:${port}`] } : {}),
|
||||
depends_on: [`${id}-mysql`],
|
||||
labels: makeLabelForServices('wordpress'),
|
||||
deploy: {
|
||||
|
@ -1,25 +1,54 @@
|
||||
import { asyncExecShell, getEngine, getUserDetails } from '$lib/common';
|
||||
import { dev } from '$app/env';
|
||||
import { checkDomainsIsValidInDNS, getDomain, getUserDetails, isDNSValid } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { t } from '$lib/translations';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const { status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
const domain = event.url.searchParams.get('domain');
|
||||
if (!domain) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
message: t.get('application.domain_required')
|
||||
}
|
||||
};
|
||||
}
|
||||
try {
|
||||
await isDNSValid(event, domain);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
const { id } = event.params;
|
||||
|
||||
let { fqdn } = await event.request.json();
|
||||
let { fqdn, forceSave, dualCerts, isDNSCheckEnabled } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
|
||||
try {
|
||||
const found = await db.isDomainConfigured({ id, fqdn });
|
||||
console.log(found);
|
||||
if (found) {
|
||||
throw {
|
||||
message: t.get('application.domain_already_in_use', {
|
||||
domain: getDomain(fqdn).replace('www.', '')
|
||||
})
|
||||
};
|
||||
}
|
||||
if (isDNSCheckEnabled && !forceSave) {
|
||||
return await checkDomainsIsValidInDNS({ event, fqdn, dualCerts });
|
||||
}
|
||||
return {
|
||||
status: found ? 500 : 200,
|
||||
body: {
|
||||
error:
|
||||
found && t.get('application.domain_already_in_use', { domain: fqdn.replace('www.', '') })
|
||||
}
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { dev } from '$app/env';
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { listSettings, ErrorHandler } from '$lib/database';
|
||||
@ -71,7 +72,8 @@ export const post: RequestHandler = async (event) => {
|
||||
minPort,
|
||||
maxPort,
|
||||
isAutoUpdateEnabled,
|
||||
isDNSCheckEnabled
|
||||
isDNSCheckEnabled,
|
||||
forceSave
|
||||
} = await event.request.json();
|
||||
try {
|
||||
const { id } = await db.listSettings();
|
||||
|
@ -31,7 +31,7 @@
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { del, post } from '$lib/api';
|
||||
import { del, get, post } from '$lib/api';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import { browser } from '$app/env';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
@ -47,7 +47,11 @@
|
||||
let minPort = settings.minPort;
|
||||
let maxPort = settings.maxPort;
|
||||
|
||||
let forceSave = false;
|
||||
let fqdn = settings.fqdn;
|
||||
let nonWWWDomain = fqdn && getDomain(fqdn).replace(/^www\./, '');
|
||||
let isNonWWWDomainOK = false;
|
||||
let isWWWDomainOK = false;
|
||||
let isFqdnSet = !!settings.fqdn;
|
||||
let loading = {
|
||||
save: false,
|
||||
@ -69,6 +73,7 @@
|
||||
}
|
||||
async function changeSettings(name) {
|
||||
try {
|
||||
resetView();
|
||||
if (name === 'isRegistrationEnabled') {
|
||||
isRegistrationEnabled = !isRegistrationEnabled;
|
||||
}
|
||||
@ -95,8 +100,10 @@
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
loading.save = true;
|
||||
nonWWWDomain = fqdn && getDomain(fqdn).replace(/^www\./, '');
|
||||
|
||||
if (fqdn !== settings.fqdn) {
|
||||
await post(`/settings/check.json`, { fqdn });
|
||||
await post(`/settings/check.json`, { fqdn, forceSave, dualCerts, isDNSCheckEnabled });
|
||||
await post(`/settings.json`, { fqdn });
|
||||
return window.location.reload();
|
||||
}
|
||||
@ -105,7 +112,22 @@
|
||||
settings.minPort = minPort;
|
||||
settings.maxPort = maxPort;
|
||||
}
|
||||
forceSave = false;
|
||||
} catch ({ error }) {
|
||||
if (error?.startsWith($t('application.dns_not_set_partial_error'))) {
|
||||
forceSave = true;
|
||||
if (dualCerts) {
|
||||
isNonWWWDomainOK = await isDNSValid(getDomain(nonWWWDomain), false);
|
||||
isWWWDomainOK = await isDNSValid(getDomain(`www.${nonWWWDomain}`), true);
|
||||
} else {
|
||||
const isWWW = getDomain(settings.fqdn).includes('www.');
|
||||
if (isWWW) {
|
||||
isWWWDomainOK = await isDNSValid(getDomain(`www.${nonWWWDomain}`), true);
|
||||
} else {
|
||||
isNonWWWDomainOK = await isDNSValid(getDomain(nonWWWDomain), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading.save = false;
|
||||
@ -119,6 +141,21 @@
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function isDNSValid(domain, isWWW) {
|
||||
try {
|
||||
await get(`/settings/check.json?domain=${domain}`);
|
||||
toast.push('DNS configuration is valid.');
|
||||
isWWW ? (isWWWDomainOK = true) : (isNonWWWDomainOK = true);
|
||||
return true;
|
||||
} catch ({ error }) {
|
||||
errorNotification(error);
|
||||
isWWW ? (isWWWDomainOK = false) : (isNonWWWDomainOK = false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function resetView() {
|
||||
forceSave = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
@ -131,11 +168,18 @@
|
||||
<div class="title font-bold">{$t('index.global_settings')}</div>
|
||||
<button
|
||||
type="submit"
|
||||
class:bg-green-600={!loading.save}
|
||||
class:bg-orange-600={forceSave}
|
||||
class:hover:bg-green-500={!loading.save}
|
||||
class:hover:bg-orange-400={forceSave}
|
||||
disabled={loading.save}
|
||||
class:bg-yellow-500={!loading.save}
|
||||
class:hover:bg-yellow-400={!loading.save}
|
||||
class="mx-2 ">{loading.save ? $t('forms.saving') : $t('forms.save')}</button
|
||||
>{loading.save
|
||||
? $t('forms.saving')
|
||||
: forceSave
|
||||
? $t('forms.confirm_continue')
|
||||
: $t('forms.save')}</button
|
||||
>
|
||||
|
||||
{#if isFqdnSet}
|
||||
<button
|
||||
on:click|preventDefault={removeFqdn}
|
||||
@ -160,11 +204,47 @@
|
||||
bind:value={fqdn}
|
||||
readonly={!$session.isAdmin || isFqdnSet}
|
||||
disabled={!$session.isAdmin || isFqdnSet}
|
||||
on:input={resetView}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
placeholder="{$t('forms.eg')}: https://coolify.io"
|
||||
/>
|
||||
|
||||
{#if forceSave}
|
||||
<div class="flex-col space-y-2 pt-4 text-center">
|
||||
{#if isNonWWWDomainOK}
|
||||
<button
|
||||
class="bg-green-600 hover:bg-green-500"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="bg-red-600 hover:bg-red-500"
|
||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
|
||||
>
|
||||
{/if}
|
||||
{#if dualCerts}
|
||||
{#if isWWWDomainOK}
|
||||
<button
|
||||
class="bg-green-600 hover:bg-green-500"
|
||||
on:click|preventDefault={() =>
|
||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="bg-red-600 hover:bg-red-500"
|
||||
on:click|preventDefault={() =>
|
||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-start py-6">
|
||||
|
@ -73,6 +73,7 @@ export const post: RequestHandler = async (event) => {
|
||||
JSON.stringify({
|
||||
buildPack: applicationFound.buildPack,
|
||||
port: applicationFound.port,
|
||||
exposePort: applicationFound.exposePort,
|
||||
installCommand: applicationFound.installCommand,
|
||||
buildCommand: applicationFound.buildCommand,
|
||||
startCommand: applicationFound.startCommand
|
||||
|
@ -46,6 +46,7 @@ export const post: RequestHandler = async (event) => {
|
||||
JSON.stringify({
|
||||
buildPack: applicationFound.buildPack,
|
||||
port: applicationFound.port,
|
||||
exposePort: applicationFound.exposePort,
|
||||
installCommand: applicationFound.installCommand,
|
||||
buildCommand: applicationFound.buildCommand,
|
||||
startCommand: applicationFound.startCommand
|
||||
|
Loading…
x
Reference in New Issue
Block a user