mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
191 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c02af6b52 | ||
|
|
6a3f4ba171 | ||
|
|
6a6426fe6b | ||
|
|
21256746c3 | ||
|
|
c34d643f95 | ||
|
|
0be402af82 | ||
|
|
b5b0b6524d | ||
|
|
22f1a3c908 | ||
|
|
fa5f439858 | ||
|
|
7cc760eecf | ||
|
|
af0652f6b2 | ||
|
|
9e009bebaa | ||
|
|
8e53ae3484 | ||
|
|
7ceb8f1537 | ||
|
|
b0eae8cfe9 | ||
|
|
febef372b8 | ||
|
|
a18e3659aa | ||
|
|
e2e342851a | ||
|
|
bee3292088 | ||
|
|
f56d4dbbb3 | ||
|
|
eccd7c96d7 | ||
|
|
4046c472ed | ||
|
|
0da4a1024a | ||
|
|
aa2f328640 | ||
|
|
4d22b610b6 | ||
|
|
e91c3eab9c | ||
|
|
2e8fd6f0c7 | ||
|
|
90fde24b40 | ||
|
|
02a1f50776 | ||
|
|
57b97a9204 | ||
|
|
1ec03693d3 | ||
|
|
4246d86694 | ||
|
|
2cce1f8459 | ||
|
|
3937cfec53 | ||
|
|
259aeeb67a | ||
|
|
9d53bc0926 | ||
|
|
1211f3c9fd | ||
|
|
c07d6aa702 | ||
|
|
4f662dbf21 | ||
|
|
a4301c5d23 | ||
|
|
86b7824c78 | ||
|
|
435f063c36 | ||
|
|
902a764ff2 | ||
|
|
4097378847 | ||
|
|
5f3567e808 | ||
|
|
7325353ced | ||
|
|
68f5b32876 | ||
|
|
8d4eaad920 | ||
|
|
4b38865cc9 | ||
|
|
030cb124e5 | ||
|
|
fd363ec017 | ||
|
|
8b813fb07a | ||
|
|
326f0dac1b | ||
|
|
828faaf2b1 | ||
|
|
9582664406 | ||
|
|
ec5474b72b | ||
|
|
62d1011d9f | ||
|
|
0a7ec6bd20 | ||
|
|
b84c37cd8f | ||
|
|
887d65e512 | ||
|
|
3543a9c809 | ||
|
|
40da3ff9fe | ||
|
|
2315192f4b | ||
|
|
0faa1540f4 | ||
|
|
00cab67e73 | ||
|
|
b92bc9eebb | ||
|
|
1905db16e8 | ||
|
|
3e9cf7285b | ||
|
|
6fdbc572fe | ||
|
|
f94e17134e | ||
|
|
3fd50ebb12 | ||
|
|
40cbee0d75 | ||
|
|
0eb7f4526e | ||
|
|
646d92757a | ||
|
|
51efa01b11 | ||
|
|
dc4a63ef92 | ||
|
|
1b717ac091 | ||
|
|
e93d97f2bc | ||
|
|
45c904e876 | ||
|
|
880865f1f2 | ||
|
|
8e42203b89 | ||
|
|
2bd91fa970 | ||
|
|
a3fd95020d | ||
|
|
e5b1ce4eef | ||
|
|
531973baab | ||
|
|
b6e6a1ccf1 | ||
|
|
1140afe2c9 | ||
|
|
f8f17832de | ||
|
|
caaf030517 | ||
|
|
106aee31bd | ||
|
|
c98ed5338a | ||
|
|
48fa4ff245 | ||
|
|
d75d2880e5 | ||
|
|
ec907b0ce4 | ||
|
|
2cda0b22c2 | ||
|
|
a0076db42e | ||
|
|
a37cf49c2a | ||
|
|
c4833c3cc2 | ||
|
|
d03fbd9224 | ||
|
|
5998212b82 | ||
|
|
62ccab22d6 | ||
|
|
5ccea1cfcc | ||
|
|
8ccb1bd34c | ||
|
|
c1a48dcf1e | ||
|
|
11d74c0c1f | ||
|
|
8290ee856f | ||
|
|
08332c8321 | ||
|
|
046f738b7d | ||
|
|
07708155ac | ||
|
|
df5e23c7c2 | ||
|
|
41adc02801 | ||
|
|
72b650b086 | ||
|
|
06fe3f33c0 | ||
|
|
cbabf7fc51 | ||
|
|
6aeafda604 | ||
|
|
30d656698e | ||
|
|
94d1af01df | ||
|
|
af97d399b6 | ||
|
|
2f90fd1fe6 | ||
|
|
c05a140b0b | ||
|
|
cbfb9a3844 | ||
|
|
5a227f70c6 | ||
|
|
44a102443d | ||
|
|
cf7fdf198d | ||
|
|
68f2f4f978 | ||
|
|
029b623f08 | ||
|
|
fe3702847a | ||
|
|
e9b852a30e | ||
|
|
1d4e5df5a2 | ||
|
|
5e14b72fe4 | ||
|
|
8ebff72cde | ||
|
|
e16643c48c | ||
|
|
65c8f55ee6 | ||
|
|
fbc81ab3eb | ||
|
|
a4d56fd79a | ||
|
|
ce45cb8aca | ||
|
|
7f8428cd17 | ||
|
|
14d79031c1 | ||
|
|
b8aa7b6d08 | ||
|
|
397ca7f20e | ||
|
|
e10b76a46b | ||
|
|
b46566280d | ||
|
|
3ab6a231eb | ||
|
|
2bc2ae9b6e | ||
|
|
2b28f8bd8f | ||
|
|
dcdac29135 | ||
|
|
591ee29e0d | ||
|
|
625e71ab08 | ||
|
|
b0af54587b | ||
|
|
be3080df08 | ||
|
|
04685c9f9d | ||
|
|
1a83f2635f | ||
|
|
630aa45c87 | ||
|
|
0c3a381d1f | ||
|
|
ffac7c5c87 | ||
|
|
410800e81c | ||
|
|
93a6518974 | ||
|
|
07aa285b27 | ||
|
|
bf01e9e29f | ||
|
|
d70672ba4b | ||
|
|
5eeb519ed6 | ||
|
|
5f047e4adf | ||
|
|
27f1e1d7cd | ||
|
|
8f3f9ebade | ||
|
|
1bd33fea98 | ||
|
|
c39cb42601 | ||
|
|
0ead17ab70 | ||
|
|
4a6062522e | ||
|
|
bd15d85732 | ||
|
|
b4bbd22781 | ||
|
|
d4c972584a | ||
|
|
edef4bd4a0 | ||
|
|
448611039c | ||
|
|
e4f701b148 | ||
|
|
8cd561b8cc | ||
|
|
a284928352 | ||
|
|
fe787538e3 | ||
|
|
360fb5ea37 | ||
|
|
13891110ce | ||
|
|
c1c25d59c8 | ||
|
|
a53bda1436 | ||
|
|
7a0d151467 | ||
|
|
a788b7bc13 | ||
|
|
8f58b14629 | ||
|
|
269250ef3d | ||
|
|
a3241516cb | ||
|
|
943300509b | ||
|
|
92d1f5aa55 | ||
|
|
614eb923d8 | ||
|
|
d910b21185 | ||
|
|
741db1778b |
@@ -2,5 +2,7 @@ COOLIFY_APP_ID=
|
||||
COOLIFY_SECRET_KEY=12341234123412341234123412341234
|
||||
COOLIFY_DATABASE_URL=file:../db/dev.db
|
||||
COOLIFY_SENTRY_DSN=
|
||||
COOLIFY_IS_ON="docker"
|
||||
COOLIFY_WHITE_LABELED="false"
|
||||
COOLIFY_IS_ON=docker
|
||||
COOLIFY_WHITE_LABELED=false
|
||||
COOLIFY_WHITE_LABELED_ICON=
|
||||
COOLIFY_AUTO_UPDATE=false
|
||||
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.
|
||||
39
.github/workflows/github-actions.yml
vendored
Normal file
39
.github/workflows/github-actions.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: release-coolify
|
||||
|
||||
on:
|
||||
release:
|
||||
types: published
|
||||
|
||||
jobs:
|
||||
make-it-coolifyed:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Get current package version
|
||||
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||
id: package-version
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: coollabsio/coolify:latest,coollabsio/coolify:${{steps.package-version.outputs.current-version}}
|
||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache
|
||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache,mode=max
|
||||
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"i18n-ally.localesPaths": ["src/lib/locales"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.extract.ignoredByFiles": {
|
||||
"src\\routes\\__layout.svelte": ["Coolify", "coolLabs logo"]
|
||||
},
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.enabledFrameworks": ["svelte"],
|
||||
"i18n-ally.enabledParsers": ["js", "ts", "json"],
|
||||
"i18n-ally.extract.autoDetect": true
|
||||
}
|
||||
259
CONTRIBUTING.md
259
CONTRIBUTING.md
@@ -1,14 +1,23 @@
|
||||
# Welcome
|
||||
# 👋 Welcome
|
||||
|
||||
First of all, thank you for considering to contribute to my project! It means a lot 💜.
|
||||
First of all, thank you for considering contributing to my project! It means a lot 💜.
|
||||
|
||||
# Technical skills required
|
||||
## 🙋 Want to help?
|
||||
|
||||
- Node.js / Javascript
|
||||
- Svelte / SvelteKit
|
||||
- Prisma.io
|
||||
If you begin in GitHub contribution, you can find the [first contribution](https://github.com/firstcontributions/first-contributions) and follow this guide.
|
||||
|
||||
# Recommended Pull Request Guideline
|
||||
Follow the [introduction](#introduction) to get started then start contributing!
|
||||
|
||||
This is a little list of what you can do to help the project:
|
||||
|
||||
- [🧑💻 Develop your own ideas](#developer-contribution)
|
||||
- [🌐 Translate the project](#translation)
|
||||
|
||||
## 👋 Introduction
|
||||
|
||||
🔴 At the moment, Coolify **doesn't support Windows**. You must use Linux or MacOS.
|
||||
|
||||
#### Recommended Pull Request Guideline
|
||||
|
||||
- Fork the project
|
||||
- Clone your fork repo to local
|
||||
@@ -16,15 +25,17 @@ First of all, thank you for considering to contribute to my project! It means a
|
||||
- Push to your fork repo
|
||||
- Create a pull request: https://github.com/coollabsio/compare
|
||||
- Write a proper description
|
||||
- Open the pull request to review
|
||||
- Open the pull request to review against `next` branch
|
||||
|
||||
---
|
||||
|
||||
# How to start after you set up your local fork?
|
||||
|
||||
This repository best with [pnpm](https://pnpm.io) due to the lock file. I recommend you should try and use `pnpm` as well, because it is cool and efficient!
|
||||
Due to the lock file, this repository is best with [pnpm](https://pnpm.io). I recommend you try and use `pnpm` because it is cool and efficient!
|
||||
|
||||
You need to have [Docker Engine](https://docs.docker.com/engine/install/) installed locally.
|
||||
|
||||
## Setup development environment
|
||||
#### Setup a local development environment
|
||||
|
||||
- Copy `.env.template` to `.env` and set the `COOLIFY_APP_ID` environment variable to something cool.
|
||||
- Install dependencies with `pnpm install`.
|
||||
@@ -33,12 +44,234 @@ You need to have [Docker Engine](https://docs.docker.com/engine/install/) instal
|
||||
- Seed the database with base entities with `pnpm db:seed`
|
||||
- You can start coding after starting `pnpm dev`.
|
||||
|
||||
## Database migrations
|
||||
#### How to start after you set up your local fork?
|
||||
|
||||
This repository works better with [pnpm](https://pnpm.io) due to the lock file. I recommend you to give it a try and use `pnpm` as well because it is cool and efficient!
|
||||
|
||||
You need to have [Docker Engine](https://docs.docker.com/engine/install/) installed locally.
|
||||
|
||||
## 🧑💻 Developer contribution
|
||||
|
||||
### Technical skills required
|
||||
|
||||
- **Languages**: Node.js / Javascript / Typescript
|
||||
- **Framework JS/TS**: Svelte / SvelteKit
|
||||
- **Database ORM**: Prisma.io
|
||||
- **Docker Engine**
|
||||
|
||||
### Database migrations
|
||||
|
||||
During development, if you change the database layout, you need to run `pnpm db:push` to migrate the database and create types for Prisma. You also need to restart the development process.
|
||||
|
||||
If the schema is finalized, you need to create a migration file with `pnpm db:migrate <nameOfMigration>` where `nameOfMigration` is given by you. Make it sense. :)
|
||||
|
||||
## Tricky parts
|
||||
### Tricky parts
|
||||
|
||||
- BullMQ, the queue system Coolify is using, cannot be hot reloaded. So if you change anything in the files related to it, you need to restart the development process. I'm actively looking of a different queue/scheduler library. I'm open for discussion!
|
||||
- BullMQ, the queue system Coolify uses, cannot be hot reloaded. So if you change anything in the files related to it, you need to restart the development process. I'm actively looking for a different queue/scheduler library. I'm open to discussion!
|
||||
|
||||
---
|
||||
|
||||
# How to add new services
|
||||
|
||||
You can add any open-source and self-hostable software (service/application) to Coolify if the following statements are true:
|
||||
|
||||
- Self-hostable (obviously)
|
||||
- Open-source
|
||||
- Maintained (I do not want to add software full of bugs)
|
||||
|
||||
## Backend
|
||||
|
||||
There are 5 steps you should make on the backend side.
|
||||
|
||||
1. Create Prisma / database schema for the new service.
|
||||
2. Add supported versions of the service.
|
||||
3. Update global functions.
|
||||
4. Create API endpoints.
|
||||
5. Define automatically generated variables.
|
||||
|
||||
> I will use [Umami](https://umami.is/) as an example service.
|
||||
|
||||
### Create Prisma / database schema for the new service.
|
||||
|
||||
You only need to do this if you store passwords or any persistent configuration. Mostly it is required by all services, but there are some exceptions, like NocoDB.
|
||||
|
||||
Update Prisma schema in [prisma/schema.prisma](prisma/schema.prisma).
|
||||
|
||||
- Add new model with the new service name.
|
||||
- Make a relationshup with `Service` model.
|
||||
- In the `Service` model, the name of the new field should be with low-capital.
|
||||
- If the service needs a database, define a `publicPort` field to be able to make it's database public, example field name in case of PostgreSQL: `postgresqlPublicPort`. It should be a optional field.
|
||||
|
||||
If you are finished with the Prisma schema, you should update the database schema with `pnpm db:push` command.
|
||||
|
||||
> You must restart the running development environment to be able to use the new model
|
||||
|
||||
> If you use VSCode, you probably need to restart the `Typescript Language Server` to get the new types loaded in the running VSCode.
|
||||
|
||||
### Add supported versions
|
||||
|
||||
Supported versions are hardcoded into Coolify (for now).
|
||||
|
||||
You need to update `supportedServiceTypesAndVersions` function at [src/lib/components/common.ts](src/lib/components/common.ts). Example JSON:
|
||||
|
||||
```js
|
||||
{
|
||||
// Name used to identify the service internally
|
||||
name: 'umami',
|
||||
// Fancier name to show to the user
|
||||
fancyName: 'Umami',
|
||||
// Docker base image for the service
|
||||
baseImage: 'ghcr.io/mikecao/umami',
|
||||
// Optional: If there is any dependent image, you should list it here
|
||||
images: [],
|
||||
// Usable tags
|
||||
versions: ['postgresql-latest'],
|
||||
// Which tag is the recommended
|
||||
recommendedVersion: 'postgresql-latest',
|
||||
// Application's default port, Umami listens on 3000
|
||||
ports: {
|
||||
main: 3000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Update global functions
|
||||
|
||||
1. Add the new service to the `include` variable in [src/lib/database/services.ts](src/lib/database/services.ts), so it will be included in all places in the database queries where it is required.
|
||||
|
||||
```js
|
||||
const include: Prisma.ServiceInclude = {
|
||||
destinationDocker: true,
|
||||
persistentStorage: true,
|
||||
serviceSecret: true,
|
||||
minio: true,
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true,
|
||||
ghost: true,
|
||||
meiliSearch: true,
|
||||
umami: true // This line!
|
||||
};
|
||||
```
|
||||
|
||||
2. Update the database update query with the new service type to `configureServiceType` function in [src/lib/database/services.ts](src/lib/database/services.ts). This function defines the automatically generated variables (passwords, users, etc.) and it's encryption process (if applicable).
|
||||
|
||||
```js
|
||||
[...]
|
||||
else if (type === 'umami') {
|
||||
const postgresqlUser = cuid();
|
||||
const postgresqlPassword = encrypt(generatePassword());
|
||||
const postgresqlDatabase = 'umami';
|
||||
const hashSalt = encrypt(generatePassword(64));
|
||||
await prisma.service.update({
|
||||
where: { id },
|
||||
data: {
|
||||
type,
|
||||
umami: {
|
||||
create: {
|
||||
postgresqlDatabase,
|
||||
postgresqlPassword,
|
||||
postgresqlUser,
|
||||
hashSalt,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
3. Add decryption process for configurations and passwords to `getService` function in [src/lib/database/services.ts](src/lib/database/services.ts)
|
||||
|
||||
```js
|
||||
if (body.umami?.postgresqlPassword)
|
||||
body.umami.postgresqlPassword = decrypt(body.umami.postgresqlPassword);
|
||||
|
||||
if (body.umami?.hashSalt) body.umami.hashSalt = decrypt(body.umami.hashSalt);
|
||||
```
|
||||
|
||||
4. Add service deletion query to `removeService` function in [src/lib/database/services.ts](src/lib/database/services.ts)
|
||||
|
||||
### Create API endpoints.
|
||||
|
||||
You need to add a new folder under [src/routes/services/[id]](src/routes/services/[id]) with the low-capital name of the service. You need 3 default files in that folder.
|
||||
|
||||
#### `index.json.ts`:
|
||||
|
||||
It has a POST endpoint that updates the service details in Coolify's database, such as name, url, other configurations, like passwords. It should look something like this:
|
||||
|
||||
```js
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
If it's necessary, you can create your own database update function, specifically for the new service.
|
||||
|
||||
#### `start.json.ts`
|
||||
|
||||
It has a POST endpoint that sets all the required secrets, persistent volumes, `docker-compose.yaml` file and sends a request to the specified docker engine.
|
||||
|
||||
You could also define an `HTTP` or `TCP` proxy for every other port that should be proxied to your server. (See `startHttpProxy` and `startTcpProxy` functions in [src/lib/haproxy/index.ts](src/lib/haproxy/index.ts))
|
||||
|
||||
#### `stop.json.ts`
|
||||
|
||||
It has a POST endpoint that stops the service and all dependent (TCP/HTTP proxies) containers. If publicPort is specified it also needs to cleanup it from the database.
|
||||
|
||||
## Frontend
|
||||
|
||||
1. You need to add a custom logo at [src/lib/components/svg/services/](src/lib/components/svg/services/) as a svelte component.
|
||||
|
||||
SVG is recommended, but you can use PNG as well. It should have the `isAbsolute` variable with the suitable CSS classes, primarily for sizing and positioning.
|
||||
|
||||
2. You need to include it the logo at
|
||||
|
||||
- [src/routes/services/index.svelte](src/routes/services/index.svelte) with `isAbsolute` in two places,
|
||||
- [src/lib/components/ServiceLinks.svelte](src/lib/components/ServiceLinks.svelte) with `isAbsolute` and a link to the docs/main site of the service
|
||||
- [src/routes/services/[id]/configuration/type.svelte](src/routes/services/[id]/configuration/type.svelte) with `isAbsolute`.
|
||||
|
||||
3. By default the URL and the name frontend forms are included in [src/routes/services/[id]/\_Services/\_Services.svelte](src/routes/services/[id]/_Services/_Services.svelte).
|
||||
|
||||
If you need to show more details on the frontend, such as users/passwords, you need to add Svelte component to [src/routes/services/[id]/\_Services](src/routes/services/[id]/_Services) with an underscore. For example, see other files in that folder.
|
||||
|
||||
You also need to add the new inputs to the `index.json.ts` file of the specific service, like for MinIO here: [src/routes/services/[id]/minio/index.json.ts](src/routes/services/[id]/minio/index.json.ts)
|
||||
|
||||
## 🌐 Translate the project
|
||||
|
||||
The project use [sveltekit-i18n](https://github.com/sveltekit-i18n/lib) to translate the project.
|
||||
It follows the [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) to name languages.
|
||||
|
||||
### Installation
|
||||
|
||||
You must have gone throw all the [intro](#introduction) steps before you can start translating.
|
||||
|
||||
It's only an advice, but I recommend you to use:
|
||||
|
||||
- Visual Studio Code
|
||||
- [i18n Ally for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=Lokalise.i18n-ally): ideal to see the progress of the translation.
|
||||
- [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode): to get the syntax color for the project
|
||||
|
||||
### Adding a language
|
||||
|
||||
If your language doesn't appear in the [locales folder list](src/lib/locales/), follow the step below:
|
||||
|
||||
1. In `src/lib/locales/`, Copy paste `en.json` and rename it with your language (eg: `cz.json`).
|
||||
2. In the [lang.json](src/lib/lang.json) file, add a line after the first bracket (`{`) with `"ISO of your language": "Language",` (eg: `"cz": "Czech",`).
|
||||
3. Have fun translating!
|
||||
|
||||
24
README.md
24
README.md
@@ -8,21 +8,30 @@ https://demo.coolify.io/
|
||||
|
||||
(If it is unresponsive, that means someone overloaded the server. 🙃)
|
||||
|
||||
## Feedback
|
||||
If you have a new service / build pack you would like to add, raise an idea [here](https://feedback.coolify.io/) to get feedback from the community!
|
||||
|
||||
## How to install
|
||||
|
||||
Installation is automated with the following command:
|
||||
|
||||
```bash
|
||||
/bin/bash -c "$(curl -fsSL https://get.coollabs.io/coolify/install.sh)"
|
||||
wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh
|
||||
```
|
||||
|
||||
If you would like no questions during installation
|
||||
If you would like no questions during installation:
|
||||
|
||||
```bash
|
||||
wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh -f
|
||||
```
|
||||
|
||||
For more details goto the [docs](https://docs.coollabs.io/coolify/installation).
|
||||
|
||||
## Features
|
||||
|
||||
### Git Sources
|
||||
|
||||
You can use the following Git Sources to be auto-deployed to your Coolifyt instance! (Self hosted versions also supported.)
|
||||
You can use the following Git Sources to be auto-deployed to your Coolifyt instance! (Self-hosted versions are also supported.)
|
||||
|
||||
- Github
|
||||
- GitLab
|
||||
@@ -38,7 +47,7 @@ You can deploy your applications to the following destinations:
|
||||
|
||||
### Applications
|
||||
|
||||
These are the predefined build packs, but with the Docker build pack, you can host basically anything that is hostable with a single Dockerfile.
|
||||
These are the predefined build packs, but with the Docker build pack, you can host anything that is hostable with a single Dockerfile.
|
||||
|
||||
- Static sites
|
||||
- NodeJS
|
||||
@@ -46,10 +55,10 @@ These are the predefined build packs, but with the Docker build pack, you can ho
|
||||
- NuxtJS
|
||||
- NextJS
|
||||
- React/Preact
|
||||
- NextJS
|
||||
- Gatsby
|
||||
- Svelte
|
||||
- PHP
|
||||
- Laravel
|
||||
- Rust
|
||||
- Docker
|
||||
|
||||
@@ -58,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
|
||||
@@ -77,6 +87,10 @@ You can host cool open-source services as well:
|
||||
- [LanguageTool](https://languagetool.org)
|
||||
- [n8n](https://n8n.io)
|
||||
- [Uptime Kuma](https://github.com/louislam/uptime-kuma)
|
||||
- [MeiliSearch](https://github.com/meilisearch/meilisearch)
|
||||
- [Umami](https://github.com/mikecao/umami)
|
||||
- [Fider](https://fider.io)
|
||||
- [Hasura](https://hasura.io)
|
||||
|
||||
## Migration from v1
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
nohup docker build -t coollabsio/prisma-engine:<arm64/amd64> --push . &
|
||||
6
data/fluentd/Dockerfile-dev
Normal file
6
data/fluentd/Dockerfile-dev
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM fluent/fluent-bit:1.9.0
|
||||
COPY fluentbit-dev.conf /tmp/fluentbit.conf
|
||||
ENTRYPOINT ["/fluent-bit/bin/fluent-bit", "-c", "/tmp/fluentbit.conf"]
|
||||
# USER root
|
||||
# RUN ["gem", "install", "fluent-plugin-mongo"]
|
||||
# USER fluent
|
||||
24
data/fluentd/fluentbit-dev.conf
Normal file
24
data/fluentd/fluentbit-dev.conf
Normal file
@@ -0,0 +1,24 @@
|
||||
[INPUT]
|
||||
Name forward
|
||||
Listen 0.0.0.0
|
||||
Port 24224
|
||||
Buffer_Chunk_Size 32KB
|
||||
Buffer_Max_Size 64KB
|
||||
|
||||
[OUTPUT]
|
||||
Name influxdb
|
||||
Match *
|
||||
Host coolify-influxdb
|
||||
Port 8086
|
||||
Bucket containerlogs
|
||||
Org organization
|
||||
HTTP_Token supertoken
|
||||
Sequence_Tag _seq
|
||||
Tag_Keys container_name
|
||||
[OUTPUT]
|
||||
Name http
|
||||
Match *
|
||||
Host host.docker.internal
|
||||
Port 3000
|
||||
URI /logs.json
|
||||
Format json
|
||||
28
data/fluentd/fluentd-dev.conf
Normal file
28
data/fluentd/fluentd-dev.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
<source>
|
||||
@type forward
|
||||
port 24224
|
||||
bind 0.0.0.0
|
||||
</source>
|
||||
|
||||
<match **>
|
||||
@type http
|
||||
endpoint http://host.docker.internal:3000/logs.json
|
||||
<buffer>
|
||||
flush_at_shutdown true
|
||||
flush_mode immediate
|
||||
flush_thread_count 8
|
||||
flush_thread_interval 1
|
||||
flush_thread_burst_interval 1
|
||||
retry_forever true
|
||||
retry_type exponential_backoff
|
||||
</buffer>
|
||||
</match>
|
||||
|
||||
<filter docker.**>
|
||||
@type parser
|
||||
key_name log
|
||||
reserve_data true
|
||||
<parse>
|
||||
@type json
|
||||
</parse>
|
||||
</filter>
|
||||
1
data/prisma/build-prisma-engine.sh
Normal file
1
data/prisma/build-prisma-engine.sh
Normal file
@@ -0,0 +1 @@
|
||||
docker build --platform linux/amd64,linux/arm64 -t coollabsio/prisma-engine -f prisma-engine.Dockerfile --push .
|
||||
@@ -2,7 +2,7 @@ FROM rust:1.58.1-alpine3.14 as prisma
|
||||
WORKDIR /prisma
|
||||
ENV RUSTFLAGS="-C target-feature=-crt-static"
|
||||
RUN apk --no-cache add openssl direnv git musl-dev openssl-dev build-base perl protoc
|
||||
RUN git clone --depth=1 --branch=3.11.x https://github.com/prisma/prisma-engines.git /prisma
|
||||
RUN git clone --depth=1 --branch=3.12.x https://github.com/prisma/prisma-engines.git /prisma
|
||||
RUN cargo build --release
|
||||
|
||||
FROM alpine
|
||||
@@ -11,7 +11,24 @@ services:
|
||||
published: 6379
|
||||
protocol: tcp
|
||||
mode: host
|
||||
|
||||
# fluentbit:
|
||||
# container_name: coolify-fluentbit
|
||||
# build:
|
||||
# context: ./data/fluentd
|
||||
# dockerfile: Dockerfile-dev
|
||||
# ports:
|
||||
# - target: 24224
|
||||
# published: 24224
|
||||
# protocol: tcp
|
||||
# mode: host
|
||||
# - target: 24224
|
||||
# published: 24224
|
||||
# protocol: udp
|
||||
# mode: host
|
||||
# networks:
|
||||
# - coolify-infra
|
||||
# extra_hosts:
|
||||
# - 'host.docker.internal:host-gateway'
|
||||
networks:
|
||||
coolify-infra:
|
||||
attachable: true
|
||||
|
||||
32
package.json
32
package.json
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "coolify",
|
||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||
"version": "2.4.10",
|
||||
"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",
|
||||
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0",
|
||||
"dev:stop": "docker-compose -f docker-compose-dev.yaml down",
|
||||
"dev:logs": "docker-compose -f docker-compose-dev.yaml logs -f --tail 10",
|
||||
"studio": "npx prisma studio",
|
||||
@@ -19,8 +19,10 @@
|
||||
"db:migrate": "COOLIFY_DATABASE_URL=file:../db/migration.db prisma migrate dev --skip-seed --name",
|
||||
"release:production:all": "cross-var docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify:$npm_package_version -t coollabsio/coolify:latest --push .",
|
||||
"release:production:amd": "cross-var docker build --platform linux/amd64 -t coollabsio/coolify:$npm_package_version -t coollabsio/coolify:latest --push .",
|
||||
"release:production:arm": "cross-var docker build --platform linux/arm64 -t coollabsio/coolify:$npm_package_version -t coollabsio/coolify:latest --push .",
|
||||
"release:staging:all": "cross-var docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify:$npm_package_version --push .",
|
||||
"release:staging:amd": "cross-var docker build --platform linux/amd64 -t coollabsio/coolify:$npm_package_version --push .",
|
||||
"release:staging:arm": "cross-var docker build --platform linux/arm64 -t coollabsio/coolify:$npm_package_version --push .",
|
||||
"release:haproxy": "docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify-haproxy-alpine:latest -t coollabsio/coolify-haproxy-alpine:1.1.0 -f data/haproxy.Dockerfile --push .",
|
||||
"release:haproxy:tcp": "docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify-haproxy-tcp-alpine:latest -t coollabsio/coolify-haproxy-tcp-alpine:1.1.0 -f data/haproxy-tcp.Dockerfile --push .",
|
||||
"release:haproxy:http": "docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify-haproxy-http-alpine:latest -t coollabsio/coolify-haproxy-http-alpine:1.1.0 -f data/haproxy-http.Dockerfile --push .",
|
||||
@@ -28,10 +30,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-node": "1.0.0-next.73",
|
||||
"@sveltejs/kit": "1.0.0-next.310",
|
||||
"@sveltejs/kit": "1.0.0-next.316",
|
||||
"@types/js-cookie": "3.0.1",
|
||||
"@types/js-yaml": "4.0.5",
|
||||
"@types/node": "17.0.23",
|
||||
"@types/node": "17.0.25",
|
||||
"@types/node-forge": "1.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "4.31.1",
|
||||
"@typescript-eslint/parser": "4.31.1",
|
||||
@@ -43,17 +45,18 @@
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-svelte3": "3.4.1",
|
||||
"husky": "7.0.4",
|
||||
"lint-staged": "12.3.7",
|
||||
"lint-staged": "12.4.0",
|
||||
"postcss": "8.4.12",
|
||||
"prettier": "2.6.2",
|
||||
"prettier-plugin-svelte": "2.7.0",
|
||||
"prettier-plugin-tailwindcss": "0.1.8",
|
||||
"prettier-plugin-tailwindcss": "0.1.10",
|
||||
"prisma": "3.11.1",
|
||||
"svelte": "3.47.0",
|
||||
"svelte-check": "2.6.0",
|
||||
"svelte-preprocess": "4.10.5",
|
||||
"svelte-check": "2.7.0",
|
||||
"svelte-preprocess": "4.10.6",
|
||||
"svelte-select": "4.4.7",
|
||||
"tailwindcss": "3.0.23",
|
||||
"sveltekit-i18n": "2.1.2",
|
||||
"tailwindcss": "3.0.24",
|
||||
"ts-node": "10.7.0",
|
||||
"tslib": "2.3.1",
|
||||
"typescript": "4.6.3"
|
||||
@@ -63,24 +66,25 @@
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@prisma/client": "3.11.1",
|
||||
"@sentry/node": "6.19.6",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bullmq": "1.80.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bullmq": "1.80.4",
|
||||
"compare-versions": "4.1.3",
|
||||
"cookie": "0.4.2",
|
||||
"cookie": "0.5.0",
|
||||
"cuid": "2.1.8",
|
||||
"dayjs": "1.11.0",
|
||||
"dayjs": "1.11.1",
|
||||
"dockerode": "3.3.1",
|
||||
"dotenv-extended": "2.9.0",
|
||||
"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",
|
||||
"mustache": "4.2.0",
|
||||
"node-forge": "1.3.1",
|
||||
"p-limit": "4.0.0",
|
||||
"svelte-kit-cookie-session": "2.1.2",
|
||||
"svelte-kit-cookie-session": "2.1.3",
|
||||
"tailwindcss-scrollbar": "0.1.0",
|
||||
"unique-names-generator": "4.7.1"
|
||||
},
|
||||
|
||||
331
pnpm-lock.yaml
generated
331
pnpm-lock.yaml
generated
@@ -1,27 +1,27 @@
|
||||
lockfileVersion: 5.3
|
||||
lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@iarna/toml': 2.2.5
|
||||
'@prisma/client': 3.11.1
|
||||
'@sentry/node': 6.19.6
|
||||
'@sveltejs/adapter-node': 1.0.0-next.73
|
||||
'@sveltejs/kit': 1.0.0-next.303
|
||||
'@sveltejs/kit': 1.0.0-next.316
|
||||
'@types/js-cookie': 3.0.1
|
||||
'@types/js-yaml': 4.0.5
|
||||
'@types/node': 17.0.23
|
||||
'@types/node': 17.0.25
|
||||
'@types/node-forge': 1.0.1
|
||||
'@typescript-eslint/eslint-plugin': 4.31.1
|
||||
'@typescript-eslint/parser': 4.31.1
|
||||
'@zerodevx/svelte-toast': 0.7.1
|
||||
autoprefixer: 10.4.4
|
||||
bcryptjs: ^2.4.3
|
||||
bullmq: 1.78.1
|
||||
bcryptjs: 2.4.3
|
||||
bullmq: 1.80.4
|
||||
compare-versions: 4.1.3
|
||||
cookie: 0.4.2
|
||||
cookie: 0.5.0
|
||||
cross-env: 7.0.3
|
||||
cross-var: 1.1.0
|
||||
cuid: 2.1.8
|
||||
dayjs: 1.11.0
|
||||
dayjs: 1.11.1
|
||||
dockerode: 3.3.1
|
||||
dotenv-extended: 2.9.0
|
||||
eslint: 7.32.0
|
||||
@@ -31,24 +31,26 @@ 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
|
||||
lint-staged: 12.3.7
|
||||
lint-staged: 12.4.0
|
||||
mustache: 4.2.0
|
||||
node-forge: 1.3.1
|
||||
p-limit: 4.0.0
|
||||
postcss: 8.4.12
|
||||
prettier: 2.6.2
|
||||
prettier-plugin-svelte: 2.7.0
|
||||
prettier-plugin-tailwindcss: 0.1.8
|
||||
prettier-plugin-tailwindcss: 0.1.10
|
||||
prisma: 3.11.1
|
||||
svelte: 3.47.0
|
||||
svelte-check: 2.6.0
|
||||
svelte-kit-cookie-session: 2.1.2
|
||||
svelte-preprocess: 4.10.5
|
||||
svelte-check: 2.7.0
|
||||
svelte-kit-cookie-session: 2.1.3
|
||||
svelte-preprocess: 4.10.6
|
||||
svelte-select: 4.4.7
|
||||
tailwindcss: 3.0.23
|
||||
sveltekit-i18n: 2.1.2
|
||||
tailwindcss: 3.0.24
|
||||
tailwindcss-scrollbar: 0.1.0
|
||||
ts-node: 10.7.0
|
||||
tslib: 2.3.1
|
||||
@@ -60,55 +62,57 @@ dependencies:
|
||||
'@prisma/client': 3.11.1_prisma@3.11.1
|
||||
'@sentry/node': 6.19.6
|
||||
bcryptjs: 2.4.3
|
||||
bullmq: 1.78.1
|
||||
bullmq: 1.80.4
|
||||
compare-versions: 4.1.3
|
||||
cookie: 0.4.2
|
||||
cookie: 0.5.0
|
||||
cuid: 2.1.8
|
||||
dayjs: 1.11.0
|
||||
dayjs: 1.11.1
|
||||
dockerode: 3.3.1
|
||||
dotenv-extended: 2.9.0
|
||||
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
|
||||
mustache: 4.2.0
|
||||
node-forge: 1.3.1
|
||||
p-limit: 4.0.0
|
||||
svelte-kit-cookie-session: 2.1.2
|
||||
tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.23
|
||||
svelte-kit-cookie-session: 2.1.3
|
||||
tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.24
|
||||
unique-names-generator: 4.7.1
|
||||
|
||||
devDependencies:
|
||||
'@sveltejs/adapter-node': 1.0.0-next.73
|
||||
'@sveltejs/kit': 1.0.0-next.303_svelte@3.47.0
|
||||
'@sveltejs/kit': 1.0.0-next.316_svelte@3.47.0
|
||||
'@types/js-cookie': 3.0.1
|
||||
'@types/js-yaml': 4.0.5
|
||||
'@types/node': 17.0.23
|
||||
'@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.3.7
|
||||
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-tailwindcss: 0.1.8_prettier@2.6.2
|
||||
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.6.0_postcss@8.4.12+svelte@3.47.0
|
||||
svelte-preprocess: 4.10.5_41810887ae6c6d59323116f47e33fa38
|
||||
svelte-check: 2.7.0_cp6olp7pwsfaq5mjijwt65d6uy
|
||||
svelte-preprocess: 4.10.6_igaqrb5onrwvsmrrc32h4m72ha
|
||||
svelte-select: 4.4.7
|
||||
tailwindcss: 3.0.23_b89136460714832cdda11d1e9d57d1ff
|
||||
ts-node: 10.7.0_ee885bc7281b682b6adbed6ae09ee090
|
||||
sveltekit-i18n: 2.1.2_svelte@3.47.0
|
||||
tailwindcss: 3.0.24_ts-node@10.7.0
|
||||
ts-node: 10.7.0_3z6inmgn4ud4moqealnfxgbl2m
|
||||
tslib: 2.3.1
|
||||
typescript: 4.6.3
|
||||
|
||||
@@ -374,10 +378,10 @@ packages:
|
||||
tiny-glob: 0.2.9
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.303_svelte@3.47.0:
|
||||
/@sveltejs/kit/1.0.0-next.316_svelte@3.47.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-WdxDc8OiF1WEd/bEza7CBdzA+3qIcCi1GKBj/gieKX9I3N8iDJt/Cg2POrLo9wQoJ47nZcAd1eOhfr7XEX1aIQ==
|
||||
integrity: sha512-oLjWOWzjriJD2t210r7ALuH/8ZADrJGsOODzRCRSJvRBCt0Q7VKVLqwKbM/RlZzD1k8Af2uRodQT11kP98hAIA==
|
||||
}
|
||||
engines: { node: '>=14.13' }
|
||||
hasBin: true
|
||||
@@ -422,6 +426,26 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@sveltekit-i18n/base/1.1.1_svelte@3.47.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-J/sMU0OwS3dCLOuilHMBqu8vZHuuXiNV9vFJx8Nb4/b5BlR/KCZ4bCXI8wZR02GHeCOYKZxWus07CM1scxa/jw==
|
||||
}
|
||||
peerDependencies:
|
||||
svelte: ^3.x
|
||||
dependencies:
|
||||
svelte: 3.47.0
|
||||
optionalDependencies:
|
||||
'@sveltekit-i18n/parser-default': 1.0.3
|
||||
dev: true
|
||||
|
||||
/@sveltekit-i18n/parser-default/1.0.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-HheveklTjp3hxpYQhoHfyA6B4bQaUeSV5MQf2usIv/58UF2jY/YqhCAWj9bDBjufbuZc5pSz4BXvdX3WVT+viA==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@szmarczak/http-timer/5.0.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -468,7 +492,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/http-cache-semantics': 4.0.1
|
||||
'@types/keyv': 3.1.3
|
||||
'@types/node': 17.0.23
|
||||
'@types/node': 17.0.25
|
||||
'@types/responselike': 1.0.0
|
||||
dev: false
|
||||
|
||||
@@ -506,7 +530,7 @@ packages:
|
||||
integrity: sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.23
|
||||
'@types/node': 17.0.25
|
||||
dev: false
|
||||
|
||||
/@types/node-forge/1.0.1:
|
||||
@@ -515,22 +539,15 @@ packages:
|
||||
integrity: sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.23
|
||||
'@types/node': 17.0.25
|
||||
dev: true
|
||||
|
||||
/@types/node/17.0.23:
|
||||
/@types/node/17.0.25:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==
|
||||
integrity: sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==
|
||||
}
|
||||
|
||||
/@types/parse-json/4.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@types/pug/2.0.5:
|
||||
resolution:
|
||||
{
|
||||
@@ -544,7 +561,7 @@ packages:
|
||||
integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.23
|
||||
'@types/node': 17.0.25
|
||||
dev: false
|
||||
|
||||
/@types/sass/1.16.1:
|
||||
@@ -553,10 +570,10 @@ packages:
|
||||
integrity: sha512-iZUcRrGuz/Tbg3loODpW7vrQJkUtpY2fFSf4ELqqkApcS2TkZ1msk7ie8iZPB86lDOP8QOTTmuvWjc5S0R9OjQ==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.23
|
||||
'@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==
|
||||
@@ -570,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
|
||||
@@ -584,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==
|
||||
@@ -605,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==
|
||||
@@ -1669,16 +1686,16 @@ packages:
|
||||
ieee754: 1.2.1
|
||||
dev: false
|
||||
|
||||
/bullmq/1.78.1:
|
||||
/bullmq/1.80.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-er45mM8nGhgA83EVCJ4PNxPyDSzakvoxeFGU4vdSgYeB+SbeFQAlJYmAC50Ms7YFPstm1LeinbVZ+oX/BmBzOg==
|
||||
integrity: sha512-j3PyjU16gqmb3Md9QjMInJdbMvxIlSJx7mojtoP06LV9MfhzW75DkDrpSuJlF0H+0+u6MViV4hhaGTxky5OJWw==
|
||||
}
|
||||
dependencies:
|
||||
cron-parser: 4.2.1
|
||||
get-port: 5.1.1
|
||||
glob: 7.2.0
|
||||
ioredis: 4.28.3
|
||||
ioredis: 4.28.5
|
||||
lodash: 4.17.21
|
||||
msgpackr: 1.4.7
|
||||
semver: 6.3.0
|
||||
@@ -1928,6 +1945,14 @@ packages:
|
||||
engines: { node: '>= 0.6' }
|
||||
dev: false
|
||||
|
||||
/cookie/0.5.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
}
|
||||
engines: { node: '>= 0.6' }
|
||||
dev: false
|
||||
|
||||
/core-js/2.6.12:
|
||||
resolution:
|
||||
{
|
||||
@@ -1937,20 +1962,6 @@ packages:
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
|
||||
/cosmiconfig/7.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==
|
||||
}
|
||||
engines: { node: '>=10' }
|
||||
dependencies:
|
||||
'@types/parse-json': 4.0.0
|
||||
import-fresh: 3.3.0
|
||||
parse-json: 5.2.0
|
||||
path-type: 4.0.0
|
||||
yaml: 1.10.2
|
||||
dev: true
|
||||
|
||||
/cpu-features/0.0.2:
|
||||
resolution:
|
||||
{
|
||||
@@ -2037,10 +2048,10 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/dayjs/1.11.0:
|
||||
/dayjs/1.11.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug==
|
||||
integrity: sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA==
|
||||
}
|
||||
dev: false
|
||||
|
||||
@@ -2296,15 +2307,6 @@ packages:
|
||||
ansi-colors: 4.1.1
|
||||
dev: true
|
||||
|
||||
/error-ex/1.3.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
|
||||
}
|
||||
dependencies:
|
||||
is-arrayish: 0.2.1
|
||||
dev: true
|
||||
|
||||
/es6-promise/3.3.1:
|
||||
resolution: { integrity: sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM= }
|
||||
dev: true
|
||||
@@ -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==
|
||||
@@ -3216,16 +3218,6 @@ packages:
|
||||
engines: { node: '>= 4' }
|
||||
dev: true
|
||||
|
||||
/import-cwd/3.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==
|
||||
}
|
||||
engines: { node: '>=8' }
|
||||
dependencies:
|
||||
import-from: 3.0.0
|
||||
dev: true
|
||||
|
||||
/import-fresh/3.3.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -3237,16 +3229,6 @@ packages:
|
||||
resolve-from: 4.0.0
|
||||
dev: true
|
||||
|
||||
/import-from/3.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==
|
||||
}
|
||||
engines: { node: '>=8' }
|
||||
dependencies:
|
||||
resolve-from: 5.0.0
|
||||
dev: true
|
||||
|
||||
/imurmurhash/0.1.4:
|
||||
resolution: { integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o= }
|
||||
engines: { node: '>=0.8.19' }
|
||||
@@ -3281,10 +3263,10 @@ packages:
|
||||
loose-envify: 1.4.0
|
||||
dev: true
|
||||
|
||||
/ioredis/4.28.3:
|
||||
/ioredis/4.28.5:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-9JOWVgBnuSxpIgfpjc1OeY1OLmA4t2KOWWURTDRXky+eWO0LZhI33pQNT9gYxANUXfh5p/zYephYni6GPRsksQ==
|
||||
integrity: sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dependencies:
|
||||
@@ -3303,9 +3285,13 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/is-arrayish/0.2.1:
|
||||
resolution: { integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= }
|
||||
dev: true
|
||||
/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:
|
||||
@@ -3365,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:
|
||||
{
|
||||
@@ -3441,13 +3437,6 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/json-parse-even-better-errors/2.3.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/json-schema-traverse/0.4.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -3547,14 +3536,18 @@ packages:
|
||||
engines: { node: '>=10' }
|
||||
dev: true
|
||||
|
||||
/lines-and-columns/1.1.6:
|
||||
resolution: { integrity: sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= }
|
||||
dev: true
|
||||
|
||||
/lint-staged/12.3.7:
|
||||
/lilconfig/2.0.5:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-/S4D726e2GIsDVWIk1XGvheCaDm1SJRQp8efamZFWJxQMVEbOwSysp7xb49Oo73KYCdy97mIWinhlxcoNqIfIQ==
|
||||
integrity: sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==
|
||||
}
|
||||
engines: { node: '>=10' }
|
||||
dev: true
|
||||
|
||||
/lint-staged/12.4.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-3X7MR0h9b7qf4iXf/1n7RlVAx+EzpAZXoCEMhVSpaBlgKDfH2ewf+QUm7BddFyq29v4dgPP+8+uYpWuSWx035A==
|
||||
}
|
||||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||
hasBin: true
|
||||
@@ -3966,10 +3959,10 @@ packages:
|
||||
path-key: 3.1.1
|
||||
dev: true
|
||||
|
||||
/object-hash/2.2.0:
|
||||
/object-hash/3.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==
|
||||
integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
|
||||
}
|
||||
engines: { node: '>= 6' }
|
||||
dev: true
|
||||
@@ -4067,19 +4060,6 @@ packages:
|
||||
callsites: 3.1.0
|
||||
dev: true
|
||||
|
||||
/parse-json/5.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
|
||||
}
|
||||
engines: { node: '>=8' }
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.12.11
|
||||
error-ex: 1.3.2
|
||||
json-parse-even-better-errors: 2.3.1
|
||||
lines-and-columns: 1.1.6
|
||||
dev: true
|
||||
|
||||
/path-is-absolute/1.0.1:
|
||||
resolution: { integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18= }
|
||||
engines: { node: '>=0.10.0' }
|
||||
@@ -4143,21 +4123,24 @@ packages:
|
||||
postcss: 8.4.12
|
||||
dev: true
|
||||
|
||||
/postcss-load-config/3.1.0_ts-node@10.7.0:
|
||||
/postcss-load-config/3.1.4_ysmyu6g5dtd6yanj6zrab4uqoy:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==
|
||||
integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
|
||||
}
|
||||
engines: { node: '>= 10' }
|
||||
peerDependencies:
|
||||
postcss: '>=8.0.9'
|
||||
ts-node: '>=9.0.0'
|
||||
peerDependenciesMeta:
|
||||
postcss:
|
||||
optional: true
|
||||
ts-node:
|
||||
optional: true
|
||||
dependencies:
|
||||
import-cwd: 3.0.0
|
||||
lilconfig: 2.0.4
|
||||
ts-node: 10.7.0_ee885bc7281b682b6adbed6ae09ee090
|
||||
lilconfig: 2.0.5
|
||||
postcss: 8.4.12
|
||||
ts-node: 10.7.0_3z6inmgn4ud4moqealnfxgbl2m
|
||||
yaml: 1.10.2
|
||||
dev: true
|
||||
|
||||
@@ -4171,13 +4154,13 @@ packages:
|
||||
postcss: ^8.2.14
|
||||
dependencies:
|
||||
postcss: 8.4.12
|
||||
postcss-selector-parser: 6.0.9
|
||||
postcss-selector-parser: 6.0.10
|
||||
dev: true
|
||||
|
||||
/postcss-selector-parser/6.0.9:
|
||||
/postcss-selector-parser/6.0.10:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==
|
||||
integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
|
||||
}
|
||||
engines: { node: '>=4' }
|
||||
dependencies:
|
||||
@@ -4212,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==
|
||||
@@ -4225,10 +4208,10 @@ packages:
|
||||
svelte: 3.47.0
|
||||
dev: true
|
||||
|
||||
/prettier-plugin-tailwindcss/0.1.8_prettier@2.6.2:
|
||||
/prettier-plugin-tailwindcss/0.1.10_prettier@2.6.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-hwarSBCswAXa+kqYtaAkFr3Vop9o04WOyZs0qo3NyvW8L7f1rif61wRyq0+ArmVThOuRBcJF5hjGXYk86cwemg==
|
||||
integrity: sha512-ooDGNuXUjgCXfShliVYQ6+0iXqUFXn+zdNInPe0WZN9qINt9srbLGFGY5jeVL4MXtY20/4S8JaBcd8l6N6NfCQ==
|
||||
}
|
||||
engines: { node: '>=12.17.0' }
|
||||
peerDependencies:
|
||||
@@ -4437,14 +4420,6 @@ packages:
|
||||
engines: { node: '>=4' }
|
||||
dev: true
|
||||
|
||||
/resolve-from/5.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
|
||||
}
|
||||
engines: { node: '>=8' }
|
||||
dev: true
|
||||
|
||||
/resolve/1.22.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -4903,10 +4878,10 @@ packages:
|
||||
engines: { node: '>= 0.4' }
|
||||
dev: true
|
||||
|
||||
/svelte-check/2.6.0_postcss@8.4.12+svelte@3.47.0:
|
||||
/svelte-check/2.7.0_cp6olp7pwsfaq5mjijwt65d6uy:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-POL3IqLUuGqb9DdvuXQaSTNXYnw/odK4hqW86+2LwGcZTdbUPKBBln7pq74wXmcnRE+12bXMY1CvbcUNa2d5nw==
|
||||
integrity: sha512-GrvG24j0+i8AOm0k0KyJ6Dqc+TAR2yzB7rtS4nljHStunVxCTr/1KYlv4EsOeoqtHLzeWMOd5D2O6nDdP/yw4A==
|
||||
}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -4919,7 +4894,7 @@ packages:
|
||||
sade: 1.7.4
|
||||
source-map: 0.7.3
|
||||
svelte: 3.47.0
|
||||
svelte-preprocess: 4.10.5_41810887ae6c6d59323116f47e33fa38
|
||||
svelte-preprocess: 4.10.6_igaqrb5onrwvsmrrc32h4m72ha
|
||||
typescript: 4.6.3
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
@@ -4945,17 +4920,17 @@ packages:
|
||||
svelte: 3.47.0
|
||||
dev: true
|
||||
|
||||
/svelte-kit-cookie-session/2.1.2:
|
||||
/svelte-kit-cookie-session/2.1.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-PfxIWDhiyYWu7iKlL0GHpmwDrdFh+rX/WmBzOuvctF25UqngIo9MCiegWBSBLE1RBwNs5UqaIeI8+vligmY07g==
|
||||
integrity: sha512-7Xk3CNbpLAi1KodlsV5W5jULQ2NxQunaXtAYqAuzIEXIq2EwC4oDa25kdmHjNe33epV0t4r0WwxZOuSdJPsapg==
|
||||
}
|
||||
dev: false
|
||||
|
||||
/svelte-preprocess/4.10.5_41810887ae6c6d59323116f47e33fa38:
|
||||
/svelte-preprocess/4.10.6_igaqrb5onrwvsmrrc32h4m72ha:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-VKXPRScCzAZqeBZOGq4LLwtNrAu++mVn7XvQox3eFDV7Ciq0Lg70Q8QWjH9iXF7J+pMlXhPsSFwpCb2E+hoeyA==
|
||||
integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==
|
||||
}
|
||||
engines: { node: '>= 9.11.2' }
|
||||
requiresBuild: true
|
||||
@@ -5022,6 +4997,19 @@ packages:
|
||||
engines: { node: '>= 8' }
|
||||
dev: true
|
||||
|
||||
/sveltekit-i18n/2.1.2_svelte@3.47.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-s5YxcbNd2EWNZaZR1A4Drt8s53E4fpUkN4XIWd3VRpw1pihZVWssqmBW1qkjQ6AB0kiu1Qwule+vt1HkbQOjrg==
|
||||
}
|
||||
peerDependencies:
|
||||
svelte: ^3.x
|
||||
dependencies:
|
||||
'@sveltekit-i18n/base': 1.1.1_svelte@3.47.0
|
||||
'@sveltekit-i18n/parser-default': 1.0.3
|
||||
svelte: 3.47.0
|
||||
dev: true
|
||||
|
||||
/table/6.7.2:
|
||||
resolution:
|
||||
{
|
||||
@@ -5037,7 +5025,7 @@ packages:
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/tailwindcss-scrollbar/0.1.0_tailwindcss@3.0.23:
|
||||
/tailwindcss-scrollbar/0.1.0_tailwindcss@3.0.24:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-egipxw4ooQDh94x02XQpPck0P0sfwazwoUGfA9SedPATIuYDR+6qe8d31Gl7YsSMRiOKDkkqfI0kBvEw9lT/Hg==
|
||||
@@ -5045,38 +5033,35 @@ packages:
|
||||
peerDependencies:
|
||||
tailwindcss: '>= 2.x.x'
|
||||
dependencies:
|
||||
tailwindcss: 3.0.23_b89136460714832cdda11d1e9d57d1ff
|
||||
tailwindcss: 3.0.24_ts-node@10.7.0
|
||||
dev: false
|
||||
|
||||
/tailwindcss/3.0.23_b89136460714832cdda11d1e9d57d1ff:
|
||||
/tailwindcss/3.0.24_ts-node@10.7.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA==
|
||||
integrity: sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==
|
||||
}
|
||||
engines: { node: '>=12.13.0' }
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
autoprefixer: ^10.0.2
|
||||
dependencies:
|
||||
arg: 5.0.1
|
||||
autoprefixer: 10.4.4_postcss@8.4.12
|
||||
chalk: 4.1.2
|
||||
chokidar: 3.5.3
|
||||
color-name: 1.1.4
|
||||
cosmiconfig: 7.0.1
|
||||
detective: 5.2.0
|
||||
didyoumean: 1.2.2
|
||||
dlv: 1.1.3
|
||||
fast-glob: 3.2.11
|
||||
glob-parent: 6.0.2
|
||||
is-glob: 4.0.3
|
||||
lilconfig: 2.0.5
|
||||
normalize-path: 3.0.0
|
||||
object-hash: 2.2.0
|
||||
object-hash: 3.0.0
|
||||
picocolors: 1.0.0
|
||||
postcss: 8.4.12
|
||||
postcss-js: 4.0.0_postcss@8.4.12
|
||||
postcss-load-config: 3.1.0_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.9
|
||||
postcss-selector-parser: 6.0.10
|
||||
postcss-value-parser: 4.2.0
|
||||
quick-lru: 5.1.1
|
||||
resolve: 1.22.0
|
||||
@@ -5148,7 +5133,7 @@ packages:
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
/ts-node/10.7.0_ee885bc7281b682b6adbed6ae09ee090:
|
||||
/ts-node/10.7.0_3z6inmgn4ud4moqealnfxgbl2m:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==
|
||||
@@ -5170,7 +5155,7 @@ packages:
|
||||
'@tsconfig/node12': 1.0.9
|
||||
'@tsconfig/node14': 1.0.1
|
||||
'@tsconfig/node16': 1.0.2
|
||||
'@types/node': 17.0.23
|
||||
'@types/node': 17.0.25
|
||||
acorn: 8.5.0
|
||||
acorn-walk: 8.2.0
|
||||
arg: 4.1.3
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Application" ADD COLUMN "exposePort" INTEGER;
|
||||
@@ -0,0 +1,12 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "ServicePersistentStorage" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"serviceId" TEXT NOT NULL,
|
||||
"path" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "ServicePersistentStorage_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_path_key" ON "ServicePersistentStorage"("serviceId", "path");
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Application" ADD COLUMN "dockerFileLocation" TEXT;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Application" ADD COLUMN "denoMainFile" TEXT;
|
||||
ALTER TABLE "Application" ADD COLUMN "denoOptions" TEXT;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Build" ADD COLUMN "branch" TEXT;
|
||||
17
prisma/migrations/20220425071132_umami/migration.sql
Normal file
17
prisma/migrations/20220425071132_umami/migration.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Umami" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"serviceId" TEXT NOT NULL,
|
||||
"postgresqlUser" TEXT NOT NULL,
|
||||
"postgresqlPassword" TEXT NOT NULL,
|
||||
"postgresqlDatabase" TEXT NOT NULL,
|
||||
"postgresqlPublicPort" INTEGER,
|
||||
"umamiAdminPassword" TEXT NOT NULL,
|
||||
"hashSalt" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Umami_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Umami_serviceId_key" ON "Umami"("serviceId");
|
||||
@@ -0,0 +1,22 @@
|
||||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_Setting" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"fqdn" TEXT,
|
||||
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||
"proxyPassword" TEXT NOT NULL,
|
||||
"proxyUser" TEXT NOT NULL,
|
||||
"proxyHash" TEXT,
|
||||
"isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting";
|
||||
DROP TABLE "Setting";
|
||||
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Application" ADD COLUMN "baseBuildImage" TEXT;
|
||||
ALTER TABLE "Application" ADD COLUMN "baseImage" TEXT;
|
||||
16
prisma/migrations/20220427133656_hasura/migration.sql
Normal file
16
prisma/migrations/20220427133656_hasura/migration.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Hasura" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"serviceId" TEXT NOT NULL,
|
||||
"postgresqlUser" TEXT NOT NULL,
|
||||
"postgresqlPassword" TEXT NOT NULL,
|
||||
"postgresqlDatabase" TEXT NOT NULL,
|
||||
"postgresqlPublicPort" INTEGER,
|
||||
"graphQLAdminPassword" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Hasura_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Hasura_serviceId_key" ON "Hasura"("serviceId");
|
||||
25
prisma/migrations/20220429202516_fider/migration.sql
Normal file
25
prisma/migrations/20220429202516_fider/migration.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Fider" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"serviceId" TEXT NOT NULL,
|
||||
"postgresqlUser" TEXT NOT NULL,
|
||||
"postgresqlPassword" TEXT NOT NULL,
|
||||
"postgresqlDatabase" TEXT NOT NULL,
|
||||
"postgresqlPublicPort" INTEGER,
|
||||
"jwtSecret" TEXT NOT NULL,
|
||||
"emailNoreply" TEXT,
|
||||
"emailMailgunApiKey" TEXT,
|
||||
"emailMailgunDomain" TEXT,
|
||||
"emailMailgunRegion" TEXT,
|
||||
"emailSmtpHost" TEXT,
|
||||
"emailSmtpPort" INTEGER,
|
||||
"emailSmtpUser" TEXT,
|
||||
"emailSmtpPassword" TEXT,
|
||||
"emailSmtpEnableStartTls" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Fider_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Fider_serviceId_key" ON "Fider"("serviceId");
|
||||
@@ -0,0 +1,29 @@
|
||||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_Fider" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"serviceId" TEXT NOT NULL,
|
||||
"postgresqlUser" TEXT NOT NULL,
|
||||
"postgresqlPassword" TEXT NOT NULL,
|
||||
"postgresqlDatabase" TEXT NOT NULL,
|
||||
"postgresqlPublicPort" INTEGER,
|
||||
"jwtSecret" TEXT NOT NULL,
|
||||
"emailNoreply" TEXT,
|
||||
"emailMailgunApiKey" TEXT,
|
||||
"emailMailgunDomain" TEXT,
|
||||
"emailMailgunRegion" TEXT NOT NULL DEFAULT 'EU',
|
||||
"emailSmtpHost" TEXT,
|
||||
"emailSmtpPort" INTEGER,
|
||||
"emailSmtpUser" TEXT,
|
||||
"emailSmtpPassword" TEXT,
|
||||
"emailSmtpEnableStartTls" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Fider_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_Fider" ("createdAt", "emailMailgunApiKey", "emailMailgunDomain", "emailMailgunRegion", "emailNoreply", "emailSmtpEnableStartTls", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", "emailSmtpUser", "id", "jwtSecret", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "serviceId", "updatedAt") SELECT "createdAt", "emailMailgunApiKey", "emailMailgunDomain", coalesce("emailMailgunRegion", 'EU') AS "emailMailgunRegion", "emailNoreply", "emailSmtpEnableStartTls", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", "emailSmtpUser", "id", "jwtSecret", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "serviceId", "updatedAt" FROM "Fider";
|
||||
DROP TABLE "Fider";
|
||||
ALTER TABLE "new_Fider" RENAME TO "Fider";
|
||||
CREATE UNIQUE INDEX "Fider_serviceId_key" ON "Fider"("serviceId");
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
||||
@@ -0,0 +1,23 @@
|
||||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_Setting" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"fqdn" TEXT,
|
||||
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||
"proxyPassword" TEXT NOT NULL,
|
||||
"proxyUser" TEXT NOT NULL,
|
||||
"proxyHash" TEXT,
|
||||
"isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting";
|
||||
DROP TABLE "Setting";
|
||||
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Service" ADD COLUMN "exposePort" INTEGER;
|
||||
@@ -1,6 +1,6 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = ["linux-musl"]
|
||||
binaryTargets = ["native", "linux-musl"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
@@ -18,6 +18,8 @@ model Setting {
|
||||
proxyPassword String
|
||||
proxyUser String
|
||||
proxyHash String?
|
||||
isAutoUpdateEnabled Boolean @default(false)
|
||||
isDNSCheckEnabled Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
@@ -82,6 +84,7 @@ model Application {
|
||||
buildPack String?
|
||||
projectId Int?
|
||||
port Int?
|
||||
exposePort Int?
|
||||
installCommand String?
|
||||
buildCommand String?
|
||||
startCommand String?
|
||||
@@ -91,6 +94,9 @@ model Application {
|
||||
pythonWSGI String?
|
||||
pythonModule String?
|
||||
pythonVariable String?
|
||||
dockerFileLocation String?
|
||||
denoMainFile String?
|
||||
denoOptions String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
settings ApplicationSettings?
|
||||
@@ -101,6 +107,8 @@ model Application {
|
||||
gitSource GitSource? @relation(fields: [gitSourceId], references: [id])
|
||||
secrets Secret[]
|
||||
persistentStorage ApplicationPersistentStorage[]
|
||||
baseImage String?
|
||||
baseBuildImage String?
|
||||
}
|
||||
|
||||
model ApplicationSettings {
|
||||
@@ -126,6 +134,17 @@ model ApplicationPersistentStorage {
|
||||
@@unique([applicationId, path])
|
||||
}
|
||||
|
||||
model ServicePersistentStorage {
|
||||
id String @id @default(cuid())
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
serviceId String
|
||||
path String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([serviceId, path])
|
||||
}
|
||||
|
||||
model Secret {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
@@ -169,6 +188,7 @@ model Build {
|
||||
githubAppId String?
|
||||
gitlabAppId String?
|
||||
commit String?
|
||||
branch String?
|
||||
status String? @default("queued")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -270,6 +290,7 @@ model Service {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
fqdn String?
|
||||
exposePort Int?
|
||||
dualCerts Boolean @default(false)
|
||||
type String?
|
||||
version String?
|
||||
@@ -285,6 +306,10 @@ model Service {
|
||||
ghost Ghost?
|
||||
serviceSecret ServiceSecret[]
|
||||
meiliSearch MeiliSearch?
|
||||
persistentStorage ServicePersistentStorage[]
|
||||
umami Umami?
|
||||
hasura Hasura?
|
||||
fider Fider?
|
||||
}
|
||||
|
||||
model PlausibleAnalytics {
|
||||
@@ -369,3 +394,52 @@ model MeiliSearch {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Umami {
|
||||
id String @id @default(cuid())
|
||||
serviceId String @unique
|
||||
postgresqlUser String
|
||||
postgresqlPassword String
|
||||
postgresqlDatabase String
|
||||
postgresqlPublicPort Int?
|
||||
umamiAdminPassword String
|
||||
hashSalt String
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Hasura {
|
||||
id String @id @default(cuid())
|
||||
serviceId String @unique
|
||||
postgresqlUser String
|
||||
postgresqlPassword String
|
||||
postgresqlDatabase String
|
||||
postgresqlPublicPort Int?
|
||||
graphQLAdminPassword String
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Fider {
|
||||
id String @id @default(cuid())
|
||||
serviceId String @unique
|
||||
postgresqlUser String
|
||||
postgresqlPassword String
|
||||
postgresqlDatabase String
|
||||
postgresqlPublicPort Int?
|
||||
jwtSecret String
|
||||
emailNoreply String?
|
||||
emailMailgunApiKey String?
|
||||
emailMailgunDomain String?
|
||||
emailMailgunRegion String @default("EU")
|
||||
emailSmtpHost String?
|
||||
emailSmtpPort Int?
|
||||
emailSmtpUser String?
|
||||
emailSmtpPassword String?
|
||||
emailSmtpEnableStartTls Boolean @default(false)
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
@@ -50,6 +50,20 @@ async function main() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set auto-update based on env variable
|
||||
const isAutoUpdateEnabled = process.env['COOLIFY_AUTO_UPDATE'] === 'true';
|
||||
const settings = await prisma.setting.findFirst({});
|
||||
if (settings) {
|
||||
await prisma.setting.update({
|
||||
where: {
|
||||
id: settings.id
|
||||
},
|
||||
data: {
|
||||
isAutoUpdateEnabled
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
main()
|
||||
.catch((e) => {
|
||||
|
||||
7
src/app.d.ts
vendored
7
src/app.d.ts
vendored
@@ -6,7 +6,11 @@ declare namespace App {
|
||||
cookies: Record<string, string>;
|
||||
}
|
||||
interface Platform {}
|
||||
interface Session extends SessionData {}
|
||||
interface Session extends SessionData {
|
||||
whiteLabelDetails: {
|
||||
icon: string | null;
|
||||
};
|
||||
}
|
||||
interface Stuff {
|
||||
service: any;
|
||||
application: any;
|
||||
@@ -27,6 +31,7 @@ interface SessionData {
|
||||
userId?: string | null;
|
||||
teamId?: string | null;
|
||||
permission?: string;
|
||||
lang?: string;
|
||||
isAdmin?: boolean;
|
||||
expires?: string | null;
|
||||
}
|
||||
|
||||
45
src/hooks.ts
45
src/hooks.ts
@@ -6,8 +6,12 @@ import { getUserDetails, sentry } from '$lib/common';
|
||||
import { version } from '$lib/common';
|
||||
import cookie from 'cookie';
|
||||
import { dev } from '$app/env';
|
||||
import { locales } from '$lib/translations';
|
||||
|
||||
const whiteLabeled = process.env['COOLIFY_WHITE_LABELED'] === 'true';
|
||||
const whiteLabelDetails = {
|
||||
icon: (whiteLabeled && process.env['COOLIFY_WHITE_LABELED_ICON']) || null
|
||||
};
|
||||
|
||||
export const handle = handleSession(
|
||||
{
|
||||
@@ -17,6 +21,24 @@ export const handle = handleSession(
|
||||
},
|
||||
async function ({ event, resolve }) {
|
||||
let response;
|
||||
|
||||
const { url, request } = event;
|
||||
|
||||
// Get defined locales
|
||||
const supportedLocales = locales.get();
|
||||
let locale;
|
||||
|
||||
if (event.locals.cookies['lang']) {
|
||||
locale = event.locals.cookies['lang'];
|
||||
} else if (!locale) {
|
||||
locale = `${`${request.headers.get('accept-language')}`.match(
|
||||
/[a-zA-Z]+?(?=-|_|,|;)/
|
||||
)}`.toLowerCase();
|
||||
}
|
||||
|
||||
// Set default locale if user preferred locale does not match
|
||||
if (!supportedLocales.includes(locale)) locale = 'en';
|
||||
|
||||
try {
|
||||
if (event.locals.cookies) {
|
||||
if (event.locals.cookies['kit.session']) {
|
||||
@@ -36,12 +58,14 @@ export const handle = handleSession(
|
||||
}
|
||||
|
||||
response = await resolve(event, {
|
||||
ssr: !event.url.pathname.startsWith('/webhooks/success')
|
||||
ssr: !event.url.pathname.startsWith('/webhooks/success'),
|
||||
transformPage: ({ html }) => html.replace(/<html.*>/, `<html lang="${locale}">`)
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
response = await resolve(event, {
|
||||
ssr: !event.url.pathname.startsWith('/webhooks/success')
|
||||
ssr: !event.url.pathname.startsWith('/webhooks/success'),
|
||||
transformPage: ({ html }) => html.replace(/<html.*>/, `<html lang="${locale}">`)
|
||||
});
|
||||
response.headers.append(
|
||||
'Set-Cookie',
|
||||
@@ -64,20 +88,31 @@ export const handle = handleSession(
|
||||
expires: new Date('Thu, 01 Jan 1970 00:00:01 GMT')
|
||||
})
|
||||
);
|
||||
} finally {
|
||||
return response;
|
||||
}
|
||||
|
||||
response.headers.append(
|
||||
'Set-Cookie',
|
||||
cookie.serialize('lang', locale, {
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
maxAge: 30 * 24 * 60 * 60
|
||||
})
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
);
|
||||
|
||||
export const getSession: GetSession = function ({ locals }) {
|
||||
return {
|
||||
lang: locals.cookies.lang,
|
||||
version,
|
||||
whiteLabeled,
|
||||
whiteLabelDetails,
|
||||
...locals.session.data
|
||||
};
|
||||
};
|
||||
|
||||
export async function handleError({ error, event }) {
|
||||
if (!dev) sentry.captureException(error, event);
|
||||
// if (!dev) sentry.captureException(error, event);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,21 @@ import { scanningTemplates } from '$lib/components/templates';
|
||||
import { promises as fs } from 'fs';
|
||||
import { staticDeployments } from '$lib/components/common';
|
||||
|
||||
const staticApps = ['static', 'react', 'vuejs', 'svelte', 'gatsby', 'astro', 'eleventy'];
|
||||
const nodeBased = [
|
||||
'react',
|
||||
'preact',
|
||||
'vuejs',
|
||||
'svelte',
|
||||
'gatsby',
|
||||
'astro',
|
||||
'eleventy',
|
||||
'node',
|
||||
'nestjs',
|
||||
'nuxtjs',
|
||||
'nextjs'
|
||||
];
|
||||
|
||||
export function makeLabelForStandaloneApplication({
|
||||
applicationId,
|
||||
fqdn,
|
||||
@@ -91,7 +106,9 @@ export const setDefaultConfiguration = async (data) => {
|
||||
startCommand,
|
||||
buildCommand,
|
||||
publishDirectory,
|
||||
baseDirectory
|
||||
baseDirectory,
|
||||
dockerFileLocation,
|
||||
denoMainFile
|
||||
} = data;
|
||||
const template = scanningTemplates[buildPack];
|
||||
if (!port) {
|
||||
@@ -102,14 +119,26 @@ export const setDefaultConfiguration = async (data) => {
|
||||
else if (buildPack === 'php') port = 80;
|
||||
else if (buildPack === 'python') port = 8000;
|
||||
}
|
||||
if (!installCommand) installCommand = template?.installCommand || 'yarn install';
|
||||
if (!startCommand) startCommand = template?.startCommand || 'yarn start';
|
||||
if (!buildCommand) buildCommand = template?.buildCommand || null;
|
||||
if (!installCommand && buildPack !== 'static' && buildPack !== 'laravel')
|
||||
installCommand = template?.installCommand || 'yarn install';
|
||||
if (!startCommand && buildPack !== 'static' && buildPack !== 'laravel')
|
||||
startCommand = template?.startCommand || 'yarn start';
|
||||
if (!buildCommand && buildPack !== 'static' && buildPack !== 'laravel')
|
||||
buildCommand = template?.buildCommand || null;
|
||||
if (!publishDirectory) publishDirectory = template?.publishDirectory || null;
|
||||
if (baseDirectory) {
|
||||
if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`;
|
||||
if (!baseDirectory.endsWith('/')) baseDirectory = `${baseDirectory}/`;
|
||||
}
|
||||
if (dockerFileLocation) {
|
||||
if (!dockerFileLocation.startsWith('/')) dockerFileLocation = `/${dockerFileLocation}`;
|
||||
if (dockerFileLocation.endsWith('/')) dockerFileLocation = dockerFileLocation.slice(0, -1);
|
||||
} else {
|
||||
dockerFileLocation = '/Dockerfile';
|
||||
}
|
||||
if (!denoMainFile) {
|
||||
denoMainFile = 'main.ts';
|
||||
}
|
||||
|
||||
return {
|
||||
buildPack,
|
||||
@@ -118,11 +147,19 @@ export const setDefaultConfiguration = async (data) => {
|
||||
startCommand,
|
||||
buildCommand,
|
||||
publishDirectory,
|
||||
baseDirectory
|
||||
baseDirectory,
|
||||
dockerFileLocation,
|
||||
denoMainFile
|
||||
};
|
||||
};
|
||||
|
||||
export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId) {
|
||||
export async function copyBaseConfigurationFiles(
|
||||
buildPack,
|
||||
workdir,
|
||||
buildId,
|
||||
applicationId,
|
||||
baseImage
|
||||
) {
|
||||
try {
|
||||
if (buildPack === 'php') {
|
||||
await fs.writeFile(`${workdir}/entrypoint.sh`, `chown -R 1000 /app`);
|
||||
@@ -131,7 +168,7 @@ export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, ap
|
||||
buildId,
|
||||
applicationId
|
||||
});
|
||||
} else if (staticDeployments.includes(buildPack)) {
|
||||
} else if (staticDeployments.includes(buildPack) && baseImage.includes('nginx')) {
|
||||
await fs.writeFile(
|
||||
`${workdir}/nginx.conf`,
|
||||
`user nginx;
|
||||
@@ -184,7 +221,6 @@ export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, ap
|
||||
}
|
||||
`
|
||||
);
|
||||
await saveBuildLog({ line: 'Copied default configuration file.', buildId, applicationId });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -199,3 +235,215 @@ export function checkPnpm(installCommand = null, buildCommand = null, startComma
|
||||
startCommand?.includes('pnpm')
|
||||
);
|
||||
}
|
||||
|
||||
export function setDefaultBaseImage(buildPack) {
|
||||
const nodeVersions = [
|
||||
{
|
||||
value: 'node:lts',
|
||||
label: 'node:lts'
|
||||
},
|
||||
{
|
||||
value: 'node:18',
|
||||
label: 'node:18'
|
||||
},
|
||||
{
|
||||
value: 'node:17',
|
||||
label: 'node:17'
|
||||
},
|
||||
{
|
||||
value: 'node:16',
|
||||
label: 'node:16'
|
||||
},
|
||||
{
|
||||
value: 'node:14',
|
||||
label: 'node:14'
|
||||
},
|
||||
{
|
||||
value: 'node:12',
|
||||
label: 'node:12'
|
||||
}
|
||||
];
|
||||
const staticVersions = [
|
||||
{
|
||||
value: 'webdevops/nginx:alpine',
|
||||
label: 'webdevops/nginx:alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/apache:alpine',
|
||||
label: 'webdevops/apache:alpine'
|
||||
}
|
||||
];
|
||||
const rustVersions = [
|
||||
{
|
||||
value: 'rust:latest',
|
||||
label: 'rust:latest'
|
||||
},
|
||||
{
|
||||
value: 'rust:1.60',
|
||||
label: 'rust:1.60'
|
||||
},
|
||||
{
|
||||
value: 'rust:1.60-buster',
|
||||
label: 'rust:1.60-buster'
|
||||
},
|
||||
{
|
||||
value: 'rust:1.60-bullseye',
|
||||
label: 'rust:1.60-bullseye'
|
||||
},
|
||||
{
|
||||
value: 'rust:1.60-slim-buster',
|
||||
label: 'rust:1.60-slim-buster'
|
||||
},
|
||||
{
|
||||
value: 'rust:1.60-slim-bullseye',
|
||||
label: 'rust:1.60-slim-bullseye'
|
||||
},
|
||||
{
|
||||
value: 'rust:1.60-alpine3.14',
|
||||
label: 'rust:1.60-alpine3.14'
|
||||
},
|
||||
{
|
||||
value: 'rust:1.60-alpine3.15',
|
||||
label: 'rust:1.60-alpine3.15'
|
||||
}
|
||||
];
|
||||
const phpVersions = [
|
||||
{
|
||||
value: 'webdevops/php-apache:8.0',
|
||||
label: 'webdevops/php-apache:8.0'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:8.0',
|
||||
label: 'webdevops/php-nginx:8.0'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:7.4',
|
||||
label: 'webdevops/php-apache:7.4'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:7.4',
|
||||
label: 'webdevops/php-nginx:7.4'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:7.3',
|
||||
label: 'webdevops/php-apache:7.3'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:7.3',
|
||||
label: 'webdevops/php-nginx:7.3'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:7.2',
|
||||
label: 'webdevops/php-apache:7.2'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:7.2',
|
||||
label: 'webdevops/php-nginx:7.2'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:7.1',
|
||||
label: 'webdevops/php-apache:7.1'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:7.1',
|
||||
label: 'webdevops/php-nginx:7.1'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:7.0',
|
||||
label: 'webdevops/php-apache:7.0'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:7.0',
|
||||
label: 'webdevops/php-nginx:7.0'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:5.6',
|
||||
label: 'webdevops/php-apache:5.6'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:5.6',
|
||||
label: 'webdevops/php-nginx:5.6'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:8.0-alpine',
|
||||
label: 'webdevops/php-apache:8.0-alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:8.0-alpine',
|
||||
label: 'webdevops/php-nginx:8.0-alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:7.4-alpine',
|
||||
label: 'webdevops/php-apache:7.4-alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:7.4-alpine',
|
||||
label: 'webdevops/php-nginx:7.4-alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:7.3-alpine',
|
||||
label: 'webdevops/php-apache:7.3-alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:7.3-alpine',
|
||||
label: 'webdevops/php-nginx:7.3-alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:7.2-alpine',
|
||||
label: 'webdevops/php-apache:7.2-alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:7.2-alpine',
|
||||
label: 'webdevops/php-nginx:7.2-alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-apache:7.1-alpine',
|
||||
label: 'webdevops/php-apache:7.1-alpine'
|
||||
},
|
||||
{
|
||||
value: 'webdevops/php-nginx:7.1-alpine',
|
||||
label: 'webdevops/php-nginx:7.1-alpine'
|
||||
}
|
||||
];
|
||||
|
||||
let payload = {
|
||||
baseImage: null,
|
||||
baseBuildImage: null,
|
||||
baseImages: [],
|
||||
baseBuildImages: []
|
||||
};
|
||||
if (nodeBased.includes(buildPack)) {
|
||||
payload.baseImage = 'node:lts';
|
||||
payload.baseImages = nodeVersions;
|
||||
payload.baseBuildImage = 'node:lts';
|
||||
payload.baseBuildImages = nodeVersions;
|
||||
}
|
||||
if (staticApps.includes(buildPack)) {
|
||||
payload.baseImage = 'webdevops/nginx:alpine';
|
||||
payload.baseImages = staticVersions;
|
||||
payload.baseBuildImage = 'node:lts';
|
||||
payload.baseBuildImages = nodeVersions;
|
||||
}
|
||||
if (buildPack === 'python') {
|
||||
payload.baseImage = 'python:3-alpine';
|
||||
}
|
||||
if (buildPack === 'rust') {
|
||||
payload.baseImage = 'rust:latest';
|
||||
payload.baseBuildImage = 'rust:latest';
|
||||
payload.baseImages = rustVersions;
|
||||
payload.baseBuildImages = rustVersions;
|
||||
}
|
||||
if (buildPack === 'deno') {
|
||||
payload.baseImage = 'denoland/deno:latest';
|
||||
}
|
||||
if (buildPack === 'php') {
|
||||
payload.baseImage = 'webdevops/php-apache:8.0-alpine';
|
||||
payload.baseImages = phpVersions;
|
||||
}
|
||||
if (buildPack === 'laravel') {
|
||||
payload.baseImage = 'webdevops/php-apache:8.0-alpine';
|
||||
payload.baseBuildImage = 'node:18';
|
||||
payload.baseBuildImages = nodeVersions;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
62
src/lib/buildPacks/deno.ts
Normal file
62
src/lib/buildPacks/deno.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const {
|
||||
workdir,
|
||||
port,
|
||||
baseDirectory,
|
||||
secrets,
|
||||
pullmergeRequestId,
|
||||
denoMainFile,
|
||||
denoOptions,
|
||||
buildId
|
||||
} = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
let depsFound = false;
|
||||
try {
|
||||
await fs.readFile(`${workdir}${baseDirectory || ''}/deps.ts`);
|
||||
depsFound = true;
|
||||
} catch (error) {}
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
if (secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (depsFound) {
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''}/deps.ts /app`);
|
||||
Dockerfile.push(`RUN deno cache deps.ts`);
|
||||
}
|
||||
Dockerfile.push(`COPY ${denoMainFile} /app`);
|
||||
Dockerfile.push(`RUN deno cache ${denoMainFile}`);
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
Dockerfile.push(`ENV NO_COLOR true`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
Dockerfile.push(`CMD deno run ${denoOptions ? denoOptions.split(' ') : ''} ${denoMainFile}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -10,19 +10,21 @@ export default async function ({
|
||||
buildId,
|
||||
baseDirectory,
|
||||
secrets,
|
||||
pullmergeRequestId
|
||||
pullmergeRequestId,
|
||||
dockerFileLocation
|
||||
}) {
|
||||
try {
|
||||
let file = `${workdir}/Dockerfile`;
|
||||
const file = `${workdir}${dockerFileLocation}`;
|
||||
let dockerFileOut = `${workdir}`;
|
||||
if (baseDirectory) {
|
||||
file = `${workdir}/${baseDirectory}/Dockerfile`;
|
||||
workdir = `${workdir}/${baseDirectory}`;
|
||||
dockerFileOut = `${workdir}${baseDirectory}`;
|
||||
workdir = `${workdir}${baseDirectory}`;
|
||||
}
|
||||
|
||||
const Dockerfile: Array<string> = (await fs.readFile(`${file}`, 'utf8'))
|
||||
.toString()
|
||||
.trim()
|
||||
.split('\n');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
@@ -41,8 +43,9 @@ export default async function ({
|
||||
}
|
||||
});
|
||||
}
|
||||
await fs.writeFile(`${file}`, Dockerfile.join('\n'));
|
||||
await buildImage({ applicationId, tag, workdir, docker, buildId, debug });
|
||||
|
||||
await fs.writeFile(`${dockerFileOut}${dockerFileLocation}`, Dockerfile.join('\n'));
|
||||
await buildImage({ applicationId, tag, workdir, docker, buildId, debug, dockerFileLocation });
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -2,25 +2,25 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, imageforBuild): Promise<void> => {
|
||||
const { applicationId, tag, workdir, publishDirectory } = data;
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${imageforBuild}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
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'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'webdevops/nginx:alpine';
|
||||
const imageForBuild = 'node:lts';
|
||||
|
||||
await buildCacheImageWithNode(data, imageForBuild);
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await buildCacheImageWithNode(data, baseImage);
|
||||
await createDockerfile(data, baseBuildImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -13,6 +13,8 @@ import rust from './rust';
|
||||
import astro from './static';
|
||||
import eleventy from './static';
|
||||
import python from './python';
|
||||
import deno from './deno';
|
||||
import laravel from './laravel';
|
||||
|
||||
export {
|
||||
node,
|
||||
@@ -29,5 +31,7 @@ export {
|
||||
rust,
|
||||
astro,
|
||||
eleventy,
|
||||
python
|
||||
python,
|
||||
deno,
|
||||
laravel
|
||||
};
|
||||
|
||||
40
src/lib/buildPacks/laravel.ts
Normal file
40
src/lib/buildPacks/laravel.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { buildCacheImageForLaravel, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const { workdir, applicationId, tag, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`ENV WEB_DOCUMENT_ROOT /app/public`);
|
||||
Dockerfile.push(`COPY --chown=application:application composer.* ./`);
|
||||
Dockerfile.push(`COPY --chown=application:application database/ database/`);
|
||||
Dockerfile.push(
|
||||
`RUN composer install --ignore-platform-reqs --no-interaction --no-plugins --no-scripts --prefer-dist`
|
||||
);
|
||||
Dockerfile.push(
|
||||
`COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/public/js/ /app/public/js/`
|
||||
);
|
||||
Dockerfile.push(
|
||||
`COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/public/css/ /app/public/css/`
|
||||
);
|
||||
Dockerfile.push(
|
||||
`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 ${port}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
try {
|
||||
await buildCacheImageForLaravel(data, baseBuildImage);
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,13 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const { applicationId, tag, port, startCommand, workdir, baseDirectory } = data;
|
||||
const { buildId, applicationId, tag, port, startCommand, workdir, baseDirectory } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
const isPnpm = startCommand.includes('pnpm');
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (isPnpm) {
|
||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
||||
@@ -22,11 +22,9 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'node:lts';
|
||||
const imageForBuild = 'node:lts';
|
||||
|
||||
await buildCacheImageWithNode(data, imageForBuild);
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await buildCacheImageWithNode(data, baseBuildImage);
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { checkPnpm } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const {
|
||||
buildId,
|
||||
workdir,
|
||||
port,
|
||||
installCommand,
|
||||
@@ -17,7 +18,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
const isPnpm = checkPnpm(installCommand, buildCommand, startCommand);
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
@@ -50,8 +51,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'node:lts';
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -11,14 +11,15 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
startCommand,
|
||||
baseDirectory,
|
||||
secrets,
|
||||
pullmergeRequestId
|
||||
pullmergeRequestId,
|
||||
buildId
|
||||
} = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
const isPnpm = checkPnpm(installCommand, buildCommand, startCommand);
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
@@ -50,8 +51,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'node:lts';
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -11,13 +11,14 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
startCommand,
|
||||
baseDirectory,
|
||||
secrets,
|
||||
pullmergeRequestId
|
||||
pullmergeRequestId,
|
||||
buildId
|
||||
} = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
const isPnpm = checkPnpm(installCommand, buildCommand, startCommand);
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
@@ -49,8 +50,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'node:lts';
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -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 } = data;
|
||||
const { workdir, baseDirectory, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
let composerFound = false;
|
||||
try {
|
||||
@@ -11,7 +11,7 @@ const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
||||
} catch (error) {}
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} /app`);
|
||||
if (htaccessFound) {
|
||||
@@ -22,12 +22,12 @@ 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'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
const { workdir, baseDirectory } = data;
|
||||
const { workdir, baseDirectory, baseImage } = data;
|
||||
try {
|
||||
let htaccessFound = false;
|
||||
try {
|
||||
@@ -36,10 +36,7 @@ export default async function (data) {
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
const image = htaccessFound
|
||||
? 'webdevops/php-apache:8.0-alpine'
|
||||
: 'webdevops/php-nginx:8.0-alpine';
|
||||
await createDockerfile(data, image, htaccessFound);
|
||||
await createDockerfile(data, baseImage, htaccessFound);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -10,12 +10,13 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
pullmergeRequestId,
|
||||
pythonWSGI,
|
||||
pythonModule,
|
||||
pythonVariable
|
||||
pythonVariable,
|
||||
buildId
|
||||
} = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
@@ -62,8 +63,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'python:3-alpine';
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -2,24 +2,25 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const { applicationId, tag, workdir, publishDirectory } = data;
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
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'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'webdevops/nginx:alpine';
|
||||
const imageForBuild = 'node:lts';
|
||||
await buildCacheImageWithNode(data, imageForBuild);
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await buildCacheImageWithNode(data, baseBuildImage);
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -4,11 +4,11 @@ import { promises as fs } from 'fs';
|
||||
import TOML from '@iarna/toml';
|
||||
|
||||
const createDockerfile = async (data, image, name): Promise<void> => {
|
||||
const { workdir, port, applicationId, tag } = data;
|
||||
const { workdir, port, applicationId, tag, buildId } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/target target`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/local/cargo /usr/local/cargo`);
|
||||
Dockerfile.push(`COPY . .`);
|
||||
@@ -27,14 +27,12 @@ const createDockerfile = async (data, image, name): Promise<void> => {
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const { workdir } = data;
|
||||
const image = 'rust:latest';
|
||||
const imageForBuild = 'rust:latest';
|
||||
const { workdir, baseImage, baseBuildImage } = data;
|
||||
const { stdout: cargoToml } = await asyncExecShell(`cat ${workdir}/Cargo.toml`);
|
||||
const parsedToml: any = TOML.parse(cargoToml);
|
||||
const name = parsedToml.package.name;
|
||||
await buildCacheImageWithCargo(data, imageForBuild);
|
||||
await createDockerfile(data, image, name);
|
||||
await buildCacheImageWithCargo(data, baseBuildImage);
|
||||
await createDockerfile(data, baseImage, name);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -10,13 +10,16 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
baseDirectory,
|
||||
publishDirectory,
|
||||
secrets,
|
||||
pullmergeRequestId
|
||||
pullmergeRequestId,
|
||||
baseImage,
|
||||
buildId,
|
||||
port
|
||||
} = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
@@ -37,17 +40,18 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
} else {
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
}
|
||||
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'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'webdevops/nginx:alpine';
|
||||
const imageForBuild = 'node:lts';
|
||||
if (data.buildCommand) await buildCacheImageWithNode(data, imageForBuild);
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
if (data.buildCommand) await buildCacheImageWithNode(data, baseBuildImage);
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -2,25 +2,25 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const { applicationId, tag, workdir, publishDirectory } = data;
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
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'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'webdevops/nginx:alpine';
|
||||
const imageForBuild = 'node:lts';
|
||||
|
||||
await buildCacheImageWithNode(data, imageForBuild);
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await buildCacheImageWithNode(data, baseBuildImage);
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -2,24 +2,25 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const { applicationId, tag, workdir, publishDirectory } = data;
|
||||
const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
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'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const image = 'webdevops/nginx:alpine';
|
||||
const imageForBuild = 'node:lts';
|
||||
await buildCacheImageWithNode(data, imageForBuild);
|
||||
await createDockerfile(data, image);
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await buildCacheImageWithNode(data, baseBuildImage);
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -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,24 +16,25 @@ 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) {
|
||||
Sentry.init({
|
||||
dsn: process.env['COOLIFY_SENTRY_DSN'],
|
||||
tracesSampleRate: 0,
|
||||
environment: 'production',
|
||||
debug: true,
|
||||
release: currentVersion,
|
||||
initialScope: {
|
||||
tags: {
|
||||
appId: process.env['COOLIFY_APP_ID'],
|
||||
'os.arch': os.arch(),
|
||||
'os.platform': os.platform(),
|
||||
'os.release': os.release()
|
||||
}
|
||||
}
|
||||
});
|
||||
// Sentry.init({
|
||||
// dsn: process.env['COOLIFY_SENTRY_DSN'],
|
||||
// tracesSampleRate: 0,
|
||||
// environment: 'production',
|
||||
// debug: true,
|
||||
// release: currentVersion,
|
||||
// initialScope: {
|
||||
// tags: {
|
||||
// appId: process.env['COOLIFY_APP_ID'],
|
||||
// 'os.arch': getOsArch(),
|
||||
// 'os.platform': os.platform(),
|
||||
// 'os.release': os.release()
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Could not initialize Sentry, no worries.');
|
||||
@@ -61,14 +64,12 @@ export const saveBuildLog = async ({
|
||||
buildId: string;
|
||||
applicationId: string;
|
||||
}): Promise<Job> => {
|
||||
if (line) {
|
||||
if (line.includes('ghs_')) {
|
||||
if (line && typeof line === 'string' && line.includes('ghs_')) {
|
||||
const regex = /ghs_.*@/g;
|
||||
line = line.replace(regex, '<SENSITIVE_DATA_DELETED>@');
|
||||
}
|
||||
const addTimestamp = `${generateTimestamp()} ${line}`;
|
||||
return await buildLogQueue.add(buildId, { buildId, line: addTimestamp, applicationId });
|
||||
}
|
||||
};
|
||||
|
||||
export const getTeam = (event: RequestEvent): string | null => {
|
||||
@@ -177,3 +178,101 @@ export function generateTimestamp(): string {
|
||||
export function getDomain(domain: string): string {
|
||||
return domain?.replace('https://', '').replace('http://', '');
|
||||
}
|
||||
|
||||
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'}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<script>
|
||||
export let service;
|
||||
import Ghost from './svg/services/Ghost.svelte';
|
||||
import Hasura from './svg/services/Hasura.svelte';
|
||||
import LanguageTool from './svg/services/LanguageTool.svelte';
|
||||
import MinIo from './svg/services/MinIO.svelte';
|
||||
import N8n from './svg/services/N8n.svelte';
|
||||
import NocoDb from './svg/services/NocoDB.svelte';
|
||||
import PlausibleAnalytics from './svg/services/PlausibleAnalytics.svelte';
|
||||
import Umami from './svg/services/Umami.svelte';
|
||||
import UptimeKuma from './svg/services/UptimeKuma.svelte';
|
||||
import VaultWarden from './svg/services/VaultWarden.svelte';
|
||||
import VsCodeServer from './svg/services/VSCodeServer.svelte';
|
||||
import Wordpress from './svg/services/Wordpress.svelte';
|
||||
import Fider from './svg/services/Fider.svelte';
|
||||
</script>
|
||||
|
||||
{#if service.type === 'plausibleanalytics'}
|
||||
@@ -52,4 +55,16 @@
|
||||
<a href="https://ghost.org" target="_blank">
|
||||
<Ghost />
|
||||
</a>
|
||||
{:else if service.type === 'umami'}
|
||||
<a href="https://umami.is" target="_blank">
|
||||
<Umami />
|
||||
</a>
|
||||
{:else if service.type === 'hasura'}
|
||||
<a href="https://hasura.io" target="_blank">
|
||||
<Hasura />
|
||||
</a>
|
||||
{:else if service.type === 'fider'}
|
||||
<a href="https://fider.io" target="_blank">
|
||||
<Fider />
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
@@ -19,7 +19,7 @@ export const staticDeployments = [
|
||||
'astro',
|
||||
'eleventy'
|
||||
];
|
||||
export const notNodeDeployments = ['php', 'docker', 'rust', 'python'];
|
||||
export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno', 'laravel'];
|
||||
|
||||
export function getDomain(domain) {
|
||||
return domain?.replace('https://', '').replace('http://', '');
|
||||
@@ -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',
|
||||
@@ -180,5 +186,46 @@ export const supportedServiceTypesAndVersions = [
|
||||
ports: {
|
||||
main: 7700
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'umami',
|
||||
fancyName: 'Umami',
|
||||
baseImage: 'ghcr.io/mikecao/umami',
|
||||
images: ['postgres:12-alpine'],
|
||||
versions: ['postgresql-latest'],
|
||||
recommendedVersion: 'postgresql-latest',
|
||||
ports: {
|
||||
main: 3000
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'hasura',
|
||||
fancyName: 'Hasura',
|
||||
baseImage: 'hasura/graphql-engine',
|
||||
images: ['postgres:12-alpine'],
|
||||
versions: ['latest', 'v2.5.1'],
|
||||
recommendedVersion: 'v2.5.1',
|
||||
ports: {
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'fider',
|
||||
fancyName: 'Fider',
|
||||
baseImage: 'getfider/fider',
|
||||
images: ['postgres:12-alpine'],
|
||||
versions: ['stable'],
|
||||
recommendedVersion: 'stable',
|
||||
ports: {
|
||||
main: 3000
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
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/applications/Deno.svelte
Normal file
24
src/lib/components/svg/applications/Deno.svelte
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 6.1 KiB |
10
src/lib/components/svg/applications/Laravel.svelte
Normal file
10
src/lib/components/svg/applications/Laravel.svelte
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg
|
||||
class="absolute top-0 left-0 -m-4 h-10 w-10"
|
||||
viewBox="0 0 50 52"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><title>Logomark</title><path
|
||||
d="M49.626 11.564a.809.809 0 0 1 .028.209v10.972a.8.8 0 0 1-.402.694l-9.209 5.302V39.25c0 .286-.152.55-.4.694L20.42 51.01c-.044.025-.092.041-.14.058-.018.006-.035.017-.054.022a.805.805 0 0 1-.41 0c-.022-.006-.042-.018-.063-.026-.044-.016-.09-.03-.132-.054L.402 39.944A.801.801 0 0 1 0 39.25V6.334c0-.072.01-.142.028-.21.006-.023.02-.044.028-.067.015-.042.029-.085.051-.124.015-.026.037-.047.055-.071.023-.032.044-.065.071-.093.023-.023.053-.04.079-.06.029-.024.055-.05.088-.069h.001l9.61-5.533a.802.802 0 0 1 .8 0l9.61 5.533h.002c.032.02.059.045.088.068.026.02.055.038.078.06.028.029.048.062.072.094.017.024.04.045.054.071.023.04.036.082.052.124.008.023.022.044.028.068a.809.809 0 0 1 .028.209v20.559l8.008-4.611v-10.51c0-.07.01-.141.028-.208.007-.024.02-.045.028-.068.016-.042.03-.085.052-.124.015-.026.037-.047.054-.071.024-.032.044-.065.072-.093.023-.023.052-.04.078-.06.03-.024.056-.05.088-.069h.001l9.611-5.533a.801.801 0 0 1 .8 0l9.61 5.533c.034.02.06.045.09.068.025.02.054.038.077.06.028.029.048.062.072.094.018.024.04.045.054.071.023.039.036.082.052.124.009.023.022.044.028.068zm-1.574 10.718v-9.124l-3.363 1.936-4.646 2.675v9.124l8.01-4.611zm-9.61 16.505v-9.13l-4.57 2.61-13.05 7.448v9.216l17.62-10.144zM1.602 7.719v31.068L19.22 48.93v-9.214l-9.204-5.209-.003-.002-.004-.002c-.031-.018-.057-.044-.086-.066-.025-.02-.054-.036-.076-.058l-.002-.003c-.026-.025-.044-.056-.066-.084-.02-.027-.044-.05-.06-.078l-.001-.003c-.018-.03-.029-.066-.042-.1-.013-.03-.03-.058-.038-.09v-.001c-.01-.038-.012-.078-.016-.117-.004-.03-.012-.06-.012-.09v-.002-21.481L4.965 9.654 1.602 7.72zm8.81-5.994L2.405 6.334l8.005 4.609 8.006-4.61-8.006-4.608zm4.164 28.764l4.645-2.674V7.719l-3.363 1.936-4.646 2.675v20.096l3.364-1.937zM39.243 7.164l-8.006 4.609 8.006 4.609 8.005-4.61-8.005-4.608zm-.801 10.605l-4.646-2.675-3.363-1.936v9.124l4.645 2.674 3.364 1.937v-9.124zM20.02 38.33l11.743-6.704 5.87-3.35-8-4.606-9.211 5.303-8.395 4.833 7.993 4.524z"
|
||||
fill="#FF2D20"
|
||||
fill-rule="evenodd"
|
||||
/></svg
|
||||
>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
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
|
||||
>
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
height="64"
|
||||
viewBox="0 0 32 32"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
><defs
|
||||
|
||||
121
src/lib/components/svg/services/Fider.svelte
Normal file
121
src/lib/components/svg/services/Fider.svelte
Normal file
@@ -0,0 +1,121 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 700 240"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={isAbsolute ? 'w-36 absolute top-0 left-0 -m-3 -mt-5' : 'w-28 mx-auto'}
|
||||
><path fill="#FDBC3D" d="m90.694 107.498-.981.39-20.608 8.23 6.332 6.547z" /><path
|
||||
fill="#8EC63F"
|
||||
d="M61.139 77.914 46.632 93 56.9 103.547c8.649-7.169 17.832-10.502 18.653-10.789L61.139 77.914z"
|
||||
/><path fill="#208ECB" d="M61.139 77.914 46.367 63.247l-14.228 14.8 14.493 14.952z" /><path
|
||||
fill="#273C8B"
|
||||
d="m40.767 57.48-6.943 2.79a38.381 38.381 0 0 0-11.742 7.418L32.14 78.047l14.228-14.8-5.601-5.768z"
|
||||
/><path
|
||||
fill="#EE4649"
|
||||
d="m119.074 138.128-.243-.25-5.653 5.675c1.897-1.516 4.287-3.66 5.896-5.425z"
|
||||
/><path
|
||||
fill="#F6944E"
|
||||
d="m102.088 150.087 3.709-1.875a46.26 46.26 0 0 0 7.381-4.659l5.653-5.676-14.311-15.285-14.493 15.072 12.061 12.423z"
|
||||
/><path fill="#FFC951" d="m90.279 107.926-14.842 14.74 14.589 14.998 14.493-15.072z" /><path
|
||||
fill="#F6CC18"
|
||||
d="m69.087 116.125-11.256 4.493c-3.301.973-6.096 2.843-8.434 5.081l11.548 11.892 14.493-14.926-6.35-6.54z"
|
||||
/><path
|
||||
fill="#C5D82D"
|
||||
d="m56.886 103.559-10.253-10.56L32 107.926l11.784 11.991c3.304-6.888 8.174-12.272 13.103-16.358z"
|
||||
/><path fill="#0D77B3" d="m32.14 78.047-14.507 14.94 14.365 14.939 14.634-14.927z" /><path
|
||||
fill="#2A377E"
|
||||
d="M32.14 78.047 22.08 67.688a38.573 38.573 0 0 0-11.093 18.455l6.645 6.843 14.506-14.94z"
|
||||
/><path
|
||||
fill="#DA2128"
|
||||
d="m94.826 162.454-4.87 5.017 14.808 15.397c-.632-1.942-1.606-4.438-2.58-6.307l-7.357-14.107z"
|
||||
/><path
|
||||
fill="#F8A561"
|
||||
d="m91.24 155.575 10.832-5.48-12.046-12.43-14.506 14.939 14.436 14.867 4.87-5.017z"
|
||||
/><path fill="#FDBC3D" d="m75.437 122.665-14.493 14.926 14.576 15.013 14.506-14.94z" /><path
|
||||
fill="#FAD412"
|
||||
d="M49.397 125.7c-6.71 6.472-9.664 16.047-9.664 16.047-.3-4.606.06-8.83.907-12.698l-8.513 8.742 14.311 14.74 14.506-14.94-11.547-11.892z"
|
||||
/><path
|
||||
fill="#C4D52D"
|
||||
d="m43.783 119.917-11.785-11.991-13.29 13.687 3.708 6.178 9.71 10 8.52-8.775a42.699 42.699 0 0 1 3.137-9.099z"
|
||||
/><path
|
||||
fill="#1B80C1"
|
||||
d="m17.633 92.986-7.638 7.72c.65 5.1 2.35 10.3 5.193 15.04l3.52 5.867 13.29-13.687-14.365-14.94z"
|
||||
/><path
|
||||
fill="#1A4685"
|
||||
d="M10.989 86.143c-1.22 4.667-1.597 9.683-.993 14.563l7.638-7.72-6.645-6.843z"
|
||||
/><path
|
||||
fill="#B12026"
|
||||
d="m89.956 197.35 12.502 13.022c4.143-8.355 5.148-18.255 2.307-27.504l-.302-.311-14.507 14.793z"
|
||||
/><path fill="#E42028" d="M89.956 167.47 75.52 182.484l14.436 14.867 14.506-14.793z" /><path
|
||||
fill="#F16B4E"
|
||||
d="m75.52 152.604-14.576 14.867 14.576 15.012 14.436-15.012z"
|
||||
/><path fill="#FAD412" d="m60.944 137.591-14.506 14.94 14.506 14.94 14.576-14.867z" /><path
|
||||
fill="#FFC951"
|
||||
d="m32.127 137.792-2.293 2.36 10.933 18.22 5.671-5.841z"
|
||||
/><path fill="#FFC951" d="m22.416 127.79 7.418 12.363 2.293-2.361z" /><path
|
||||
fill="#981C20"
|
||||
d="M102.458 210.371 89.955 197.35 75.45 212.29l12.918 13.304a36.951 36.951 0 0 0 14.09-15.222z"
|
||||
/><path
|
||||
fill="#C92039"
|
||||
d="m75.52 182.483-12.59 12.823 6.423 10.704 6.097 6.28 14.506-14.94z"
|
||||
/><path fill="#F05B41" d="m60.944 167.47-9.096 9.369 11.081 18.467 12.59-12.823z" /><path
|
||||
fill="#F6CC18"
|
||||
d="m46.438 152.53-5.671 5.842 11.081 18.467 9.096-9.368z"
|
||||
/><path
|
||||
fill="#7A1319"
|
||||
d="m74.01 213.772 8.904 14.838 4.104-2.237c.429-.233.934-.533 1.35-.78L75.45 212.29l-1.44 1.482z"
|
||||
/><path fill="#981C20" d="m69.353 206.01 4.658 7.762 1.44-1.482z" /><path
|
||||
fill="#15796E"
|
||||
d="m147.842 48.094 10.653-10.971a41.81 41.81 0 0 0 .943-6.94l-11.414-11.755-14.48 14.94 14.298 14.726z"
|
||||
/><path fill="#29B364" d="m133.53 33.354 14.494-14.926-2.737-2.965-20.95 8.422z" /><path
|
||||
fill="#21A29F"
|
||||
d="M151.819 52.189c3.057-4.334 5.434-9.932 6.677-15.066l-10.653 10.971 3.976 4.095z"
|
||||
/><path
|
||||
fill="#12827F"
|
||||
d="M159.438 30.183c.307-6.28-.783-12.862-3.488-19.006l-1.41.567-6.516 6.684 11.414 11.755zM154.54 11.744l-9.253 3.72 2.737 2.964z"
|
||||
/><path fill="#0C6355" d="m133.336 63.034 14.506-14.94-14.311-14.713-14.493 14.926z" /><path
|
||||
fill="#1B974D"
|
||||
d="m104.532 33.368 14.506 14.94 14.48-14.94-9.2-9.476-17.363 6.98z"
|
||||
/><path fill="#16669F" d="m106.955 30.872-3.485 1.401 1.062 1.095z" /><path
|
||||
fill="#44BFBD"
|
||||
d="M135.9 65.674A41.696 41.696 0 0 0 151.82 52.19l-3.977-4.095-14.506 14.94 2.564 2.64z"
|
||||
/><path
|
||||
fill="#0D5650"
|
||||
d="m115.71 74.76 11.052-4.956 6.574-6.77-14.298-14.727-14.506 14.94z"
|
||||
/><path fill="#3FAF49" d="m119.038 48.307-14.506-14.94-14.576 14.868 14.563 14.999z" /><path
|
||||
fill="#0D77B3"
|
||||
d="m104.532 33.368-1.062-1.095-20.97 8.43 7.456 7.532z"
|
||||
/><path
|
||||
fill="#0C6355"
|
||||
d="M134.766 66.217c.352-.157.789-.376 1.134-.543l-2.564-2.64-6.574 6.77 8.004-3.587z"
|
||||
/><path fill="#12827F" d="m115.71 74.76-11.178-11.513-14.506 14.94 5.47 5.633z" /><path
|
||||
fill="#4EB648"
|
||||
d="M104.532 63.247 89.956 48.235 75.52 63.247l14.493 14.927z"
|
||||
/><path fill="#16669F" d="M89.956 48.235 82.5 40.703l-20.868 8.388L75.52 63.247z" /><path
|
||||
fill="#FBB139"
|
||||
d="M129.526 119.012c1.902-7.144 2.108-15.019.353-22.538l-11.048 11.379 10.695 11.16z"
|
||||
/><path
|
||||
fill="#E2B523"
|
||||
d="m110.62 99.542 8.21 8.311 11.049-11.38a46.303 46.303 0 0 0-1.186-4.149l-18.074 7.218z"
|
||||
/><path fill="#189590" d="M90.026 78.186 76.128 92.501l19.367-8.681z" /><path
|
||||
fill="#8EC63F"
|
||||
d="m76.083 92.521 13.943-14.335-14.506-14.94-14.381 14.668 14.413 14.844z"
|
||||
/><path
|
||||
fill="#0D77B3"
|
||||
d="M75.52 63.247 61.633 49.09l-2.264.91-13.002 13.246L61.14 77.914z"
|
||||
/><path fill="#1953A2" d="m59.37 50.002-18.603 7.477 5.6 5.768z" /><path
|
||||
fill="#ED3551"
|
||||
d="M119.324 137.84c.885-.988 2.15-2.59 2.942-3.646l-3.17 3.41.228.236z"
|
||||
/><path
|
||||
fill="#F8A561"
|
||||
d="m118.83 137.877 3.437-3.683a46.268 46.268 0 0 0 7.259-15.182l-10.695-11.159-14.311 14.74 14.31 15.284z"
|
||||
/><path
|
||||
fill="#E9B520"
|
||||
d="m90.279 107.926 14.24 14.666 14.312-14.739-8.212-8.311-19.925 7.956z"
|
||||
/><path
|
||||
fill="#EE4649"
|
||||
d="m118.83 137.877.244.251c.085-.095.166-.193.25-.288l-.228-.235-.265.272z"
|
||||
/></svg
|
||||
>
|
||||
26
src/lib/components/svg/services/Hasura.svelte
Normal file
26
src/lib/components/svg/services/Hasura.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'}
|
||||
viewBox="0 0 81 84"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_5273_21928)">
|
||||
<path
|
||||
d="M79.7186 28.6019C82.1218 21.073 80.6778 6.03601 76.0158 0.487861C75.4073 -0.238064 74.2624 -0.134361 73.757 0.664158L68.0121 9.72786C66.5887 11.5427 64.0308 11.9575 62.1124 10.6923C55.8827 6.59601 48.4359 4.21082 40.4322 4.21082C32.4285 4.21082 24.9817 6.59601 18.752 10.6923C16.8336 11.9575 14.2757 11.5323 12.8523 9.72786L7.10738 0.664158C6.60199 -0.134361 5.45712 -0.238064 4.84859 0.487861C0.186621 6.03601 -1.25735 21.073 1.14583 28.6019C1.94002 31.1012 2.16693 33.7456 1.69248 36.3279C1.22834 38.879 0.753897 41.9693 0.753897 44.1056C0.753897 66.1323 18.5251 84.0004 40.4322 84.0004C62.3497 84.0004 80.1105 66.1427 80.1105 44.1056C80.1105 41.959 79.6464 38.879 79.1719 36.3279C78.6975 33.7456 78.9244 31.1012 79.7186 28.6019ZM40.4322 75.0819C23.4965 75.0819 9.71684 61.2271 9.71684 44.199C9.71684 43.639 9.73747 43.0893 9.7581 42.5397C10.3769 30.9353 17.3802 21.0108 27.3024 16.2819C31.2836 14.3738 35.7393 13.316 40.4322 13.316C45.1251 13.316 49.5808 14.3842 53.5724 16.2923C63.4945 21.0212 70.4978 30.9456 71.1166 42.5397C71.1476 43.0893 71.1579 43.639 71.1579 44.199C71.1476 61.2271 57.3679 75.0819 40.4322 75.0819Z"
|
||||
fill="#1EB4D4"
|
||||
/>
|
||||
<path
|
||||
d="M53.7371 56.083L45.8881 42.4044L39.153 30.997C38.9983 30.7274 38.7095 30.5615 38.3898 30.5615H31.9538C31.634 30.5615 31.3452 30.7378 31.1905 31.0074C31.0358 31.2874 31.0358 31.6296 31.2008 31.8993L37.6368 42.7881L28.9936 56.0415C28.8183 56.3111 28.7977 56.6637 28.9524 56.9541C29.1071 57.2444 29.4062 57.4207 29.7259 57.4207H36.2032C36.5023 57.4207 36.7808 57.2652 36.9458 57.0163L41.6181 49.6741L45.8056 56.9748C45.9603 57.2548 46.2594 57.4207 46.5688 57.4207H52.9533C53.273 57.4207 53.5618 57.2548 53.7165 56.9748C53.9022 56.6948 53.9022 56.363 53.7371 56.083Z"
|
||||
fill="#1EB4D4"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5273_21928">
|
||||
<rect width="81" height="84" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
83
src/lib/components/svg/services/Umami.svelte
Normal file
83
src/lib/components/svg/services/Umami.svelte
Normal file
@@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
version="1.0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 856.000000 856.000000"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'}
|
||||
>
|
||||
<metadata> Created by potrace 1.11, written by Peter Selinger 2001-2013 </metadata>
|
||||
<g
|
||||
transform="translate(0.000000,856.000000) scale(0.100000,-0.100000)"
|
||||
fill="currentColor"
|
||||
stroke="none"
|
||||
>
|
||||
<path
|
||||
d="M4027 8163 c-2 -2 -28 -5 -58 -7 -50 -4 -94 -9 -179 -22 -19 -2 -48
|
||||
-6 -65 -9 -47 -6 -236 -44 -280 -55 -22 -6 -49 -12 -60 -15 -34 -6 -58 -13
|
||||
-130 -36 -38 -13 -72 -23 -75 -24 -29 -6 -194 -66 -264 -96 -49 -22 -95 -39
|
||||
-102 -39 -7 0 -19 -7 -28 -15 -8 -9 -18 -15 -21 -14 -7 1 -197 -92 -205 -101
|
||||
-3 -3 -21 -13 -40 -24 -79 -42 -123 -69 -226 -137 -94 -62 -246 -173 -280
|
||||
-204 -6 -5 -29 -25 -52 -43 -136 -111 -329 -305 -457 -462 -21 -25 -41 -47
|
||||
-44 -50 -4 -3 -22 -26 -39 -52 -18 -25 -38 -52 -45 -60 -34 -35 -207 -308
|
||||
-259 -408 -13 -25 -25 -47 -28 -50 -11 -11 -121 -250 -159 -346 -42 -105 -114
|
||||
-321 -126 -374 l-7 -30 -263 0 c-245 0 -268 -2 -321 -21 -94 -35 -171 -122
|
||||
-191 -216 -9 -39 -8 -852 0 -938 9 -87 16 -150 23 -195 3 -19 6 -48 8 -65 3
|
||||
-29 14 -97 22 -140 3 -11 7 -36 10 -55 3 -19 9 -51 14 -70 5 -19 11 -46 14
|
||||
-60 29 -138 104 -401 145 -505 5 -11 23 -58 42 -105 18 -47 42 -105 52 -130
|
||||
11 -25 21 -49 22 -55 3 -10 109 -224 164 -330 18 -33 50 -89 71 -124 22 -34
|
||||
40 -64 40 -66 0 -8 104 -161 114 -167 6 -4 7 -8 3 -8 -4 0 4 -12 18 -27 14
|
||||
-15 25 -32 25 -36 0 -5 6 -14 13 -21 6 -7 21 -25 32 -41 11 -15 34 -44 50 -64
|
||||
17 -21 41 -52 55 -70 13 -18 33 -43 45 -56 11 -13 42 -49 70 -81 100 -118 359
|
||||
-369 483 -469 34 -27 62 -53 62 -57 0 -5 6 -8 13 -8 7 0 19 -9 27 -20 8 -11
|
||||
19 -20 26 -20 6 0 19 -9 29 -20 10 -11 22 -20 27 -20 5 0 23 -13 41 -30 18
|
||||
-16 37 -30 44 -30 6 0 13 -4 15 -8 3 -8 186 -132 194 -132 2 0 27 -15 56 -34
|
||||
132 -83 377 -207 558 -280 36 -15 74 -31 85 -36 62 -26 220 -81 320 -109 79
|
||||
-23 191 -53 214 -57 14 -3 28 -7 31 -9 4 -2 20 -7 36 -9 16 -3 40 -8 54 -11
|
||||
14 -3 36 -8 50 -11 14 -2 36 -7 50 -10 13 -3 40 -8 60 -10 19 -2 46 -7 60 -10
|
||||
54 -10 171 -25 320 -40 90 -9 613 -12 636 -4 11 5 28 4 37 -1 9 -6 17 -6 17
|
||||
-1 0 4 10 8 23 9 29 0 154 12 192 18 17 3 46 7 65 9 70 10 131 20 183 32 16 3
|
||||
38 7 50 9 45 7 165 36 252 60 50 14 100 28 112 30 12 3 34 10 48 15 14 5 25 7
|
||||
25 4 0 -4 6 -2 13 3 6 6 30 16 52 22 22 7 47 15 55 18 8 4 17 7 20 7 10 2 179
|
||||
68 240 94 96 40 342 159 395 191 17 10 53 30 80 46 28 15 81 47 118 71 37 24
|
||||
72 44 76 44 5 0 11 3 13 8 2 4 30 25 63 47 33 22 62 42 65 45 3 3 50 38 105
|
||||
79 55 40 105 79 110 85 6 6 24 22 40 34 85 65 465 430 465 447 0 3 8 13 18 23
|
||||
9 10 35 40 57 66 22 27 47 56 55 65 8 9 42 52 74 96 32 44 71 96 85 115 140
|
||||
183 358 576 461 830 12 30 28 69 36 85 24 56 123 355 117 355 -3 0 -1 6 5 13
|
||||
6 6 14 30 18 52 10 48 9 46 17 65 5 13 37 155 52 230 9 42 35 195 40 231 34
|
||||
235 40 357 40 804 l0 420 -24 44 c-46 87 -143 157 -231 166 -19 2 -144 4 -276
|
||||
4 l-242 1 -36 118 c-21 64 -46 139 -56 166 -11 27 -20 52 -20 57 0 5 -11 33
|
||||
-25 63 -14 30 -25 58 -25 61 0 18 -152 329 -162 333 -5 2 -8 10 -8 18 0 8 -4
|
||||
14 -10 14 -5 0 -9 3 -8 8 3 9 -40 82 -128 217 -63 97 -98 145 -187 259 -133
|
||||
171 -380 420 -559 564 -71 56 -132 102 -138 102 -5 0 -10 3 -10 8 0 4 -25 23
|
||||
-55 42 -30 19 -55 38 -55 43 0 4 -6 7 -13 7 -7 0 -22 8 -33 18 -11 9 -37 26
|
||||
-59 37 -21 11 -44 25 -50 30 -41 37 -413 220 -540 266 -27 9 -61 22 -75 27
|
||||
-14 5 -28 10 -32 11 -4 1 -28 10 -53 21 -25 11 -46 19 -48 18 -2 -1 -109 29
|
||||
-137 40 -13 4 -32 9 -65 16 -5 1 -16 5 -22 9 -7 5 -13 6 -13 3 0 -2 -15 0 -32
|
||||
5 -18 5 -44 11 -58 14 -14 3 -36 7 -50 10 -14 3 -50 9 -80 15 -30 6 -64 12
|
||||
-75 14 -11 2 -45 6 -75 10 -30 4 -71 9 -90 12 -19 3 -53 6 -75 7 -22 1 -44 5
|
||||
-50 8 -11 7 -542 9 -548 2z m57 -404 c7 10 436 8 511 -3 22 -3 60 -8 85 -11
|
||||
25 -2 56 -6 70 -9 14 -2 43 -7 65 -10 38 -5 58 -9 115 -21 14 -3 34 -7 45 -9
|
||||
11 -2 58 -14 105 -26 47 -12 92 -23 100 -25 35 -7 279 -94 308 -109 17 -9 34
|
||||
-16 37 -16 3 1 20 -6 38 -14 17 -8 68 -31 112 -51 44 -20 82 -35 84 -35 2 1 7
|
||||
-3 10 -8 3 -5 43 -28 88 -51 45 -23 87 -48 93 -56 7 -8 17 -15 22 -15 12 0
|
||||
192 -121 196 -132 2 -4 8 -8 13 -8 10 0 119 -86 220 -172 102 -87 256 -244
|
||||
349 -357 25 -30 53 -63 63 -73 9 -10 17 -22 17 -28 0 -5 3 -10 8 -10 4 0 25
|
||||
-27 46 -60 22 -33 43 -60 48 -60 4 0 8 -5 8 -11 0 -6 11 -25 25 -43 14 -18 25
|
||||
-38 25 -44 0 -7 4 -12 8 -12 5 0 16 -15 25 -32 9 -18 30 -55 47 -83 46 -77
|
||||
161 -305 154 -305 -4 0 -2 -6 4 -12 6 -7 23 -47 40 -88 16 -41 33 -84 37 -95
|
||||
5 -11 9 -22 10 -25 0 -3 11 -36 24 -73 13 -38 21 -70 19 -73 -3 -2 -1386 -3
|
||||
-3075 -2 l-3071 3 38 110 c47 137 117 301 182 425 62 118 167 295 191 320 9
|
||||
11 17 22 17 25 0 7 39 63 58 83 6 7 26 35 44 60 18 26 37 52 43 57 6 6 34 37
|
||||
61 70 48 59 271 286 329 335 17 14 53 43 80 65 28 22 52 42 55 45 3 3 21 17
|
||||
40 30 19 14 40 28 45 32 40 32 105 78 109 78 3 0 28 16 55 35 26 19 53 35 58
|
||||
35 5 0 18 8 29 18 17 15 53 35 216 119 118 60 412 176 422 166 3 -4 6 -2 6 4
|
||||
0 6 12 13 28 16 15 3 52 12 82 21 30 9 63 19 73 21 10 2 27 7 37 10 10 3 29 8
|
||||
42 10 13 3 48 10 78 16 30 7 61 12 68 12 6 0 12 4 12 9 0 5 5 6 10 3 6 -4 34
|
||||
-2 63 4 51 11 71 13 197 26 36 4 67 9 69 11 2 2 10 -1 17 -7 8 -6 14 -7 18 0z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -15,7 +15,6 @@ export function findBuildPack(pack, packageManager = 'npm') {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: null
|
||||
};
|
||||
@@ -153,6 +152,26 @@ export function findBuildPack(pack, packageManager = 'npm') {
|
||||
port: 8000
|
||||
};
|
||||
}
|
||||
if (pack === 'deno') {
|
||||
return {
|
||||
...metaData,
|
||||
installCommand: null,
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: 8000
|
||||
};
|
||||
}
|
||||
if (pack === 'laravel') {
|
||||
return {
|
||||
...metaData,
|
||||
installCommand: null,
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: 'node',
|
||||
fancyName: 'Node.js',
|
||||
@@ -178,18 +197,25 @@ export const buildPacks = [
|
||||
hoverColor: 'hover:bg-orange-700',
|
||||
color: 'bg-orange-700'
|
||||
},
|
||||
{
|
||||
name: 'docker',
|
||||
fancyName: 'Docker',
|
||||
hoverColor: 'hover:bg-sky-700',
|
||||
color: 'bg-sky-700'
|
||||
},
|
||||
|
||||
{
|
||||
name: 'php',
|
||||
fancyName: 'PHP',
|
||||
hoverColor: 'hover:bg-indigo-700',
|
||||
color: 'bg-indigo-700'
|
||||
},
|
||||
{
|
||||
name: 'laravel',
|
||||
fancyName: 'Laravel',
|
||||
hoverColor: 'hover:bg-indigo-700',
|
||||
color: 'bg-indigo-700'
|
||||
},
|
||||
{
|
||||
name: 'docker',
|
||||
fancyName: 'Docker',
|
||||
hoverColor: 'hover:bg-sky-700',
|
||||
color: 'bg-sky-700'
|
||||
},
|
||||
{
|
||||
name: 'svelte',
|
||||
fancyName: 'Svelte',
|
||||
@@ -262,6 +288,12 @@ export const buildPacks = [
|
||||
fancyName: 'Python',
|
||||
hoverColor: 'hover:bg-green-700',
|
||||
color: 'bg-green-700'
|
||||
},
|
||||
{
|
||||
name: 'deno',
|
||||
fancyName: 'Deno',
|
||||
hoverColor: 'hover:bg-green-700',
|
||||
color: 'bg-green-700'
|
||||
}
|
||||
];
|
||||
export const scanningTemplates = {
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
Application,
|
||||
ApplicationPersistentStorage
|
||||
} from '@prisma/client';
|
||||
import { setDefaultBaseImage } from '$lib/buildPacks/common';
|
||||
|
||||
export async function listApplications(teamId: string): Promise<Application[]> {
|
||||
if (teamId === '0') {
|
||||
@@ -137,7 +138,18 @@ export async function getApplicationWebhook({
|
||||
return s;
|
||||
});
|
||||
}
|
||||
return { ...application };
|
||||
const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage(
|
||||
application.buildPack
|
||||
);
|
||||
|
||||
// Set default build images
|
||||
if (!application.baseImage) {
|
||||
application.baseImage = baseImage;
|
||||
}
|
||||
if (!application.baseBuildImage) {
|
||||
application.baseBuildImage = baseBuildImage;
|
||||
}
|
||||
return { ...application, baseBuildImages, baseImages };
|
||||
} catch (e) {
|
||||
throw { status: 404, body: { message: e.message } };
|
||||
}
|
||||
@@ -195,8 +207,18 @@ export async function getApplication({ id, teamId }: { id: string; teamId: strin
|
||||
return s;
|
||||
});
|
||||
}
|
||||
const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage(
|
||||
body.buildPack
|
||||
);
|
||||
|
||||
return { ...body };
|
||||
// Set default build images
|
||||
if (!body.baseImage) {
|
||||
body.baseImage = baseImage;
|
||||
}
|
||||
if (!body.baseBuildImage) {
|
||||
body.baseBuildImage = baseBuildImage;
|
||||
}
|
||||
return { ...body, baseBuildImages, baseImages };
|
||||
}
|
||||
|
||||
export async function configureGitRepository({
|
||||
@@ -256,6 +278,7 @@ export async function configureApplication({
|
||||
name,
|
||||
fqdn,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@@ -263,13 +286,19 @@ export async function configureApplication({
|
||||
publishDirectory,
|
||||
pythonWSGI,
|
||||
pythonModule,
|
||||
pythonVariable
|
||||
pythonVariable,
|
||||
dockerFileLocation,
|
||||
denoMainFile,
|
||||
denoOptions,
|
||||
baseImage,
|
||||
baseBuildImage
|
||||
}: {
|
||||
id: string;
|
||||
buildPack: string;
|
||||
name: string;
|
||||
fqdn: string;
|
||||
port: number;
|
||||
exposePort: number;
|
||||
installCommand: string;
|
||||
buildCommand: string;
|
||||
startCommand: string;
|
||||
@@ -278,6 +307,11 @@ export async function configureApplication({
|
||||
pythonWSGI: string;
|
||||
pythonModule: string;
|
||||
pythonVariable: string;
|
||||
dockerFileLocation: string;
|
||||
denoMainFile: string;
|
||||
denoOptions: string;
|
||||
baseImage: string;
|
||||
baseBuildImage: string;
|
||||
}): Promise<Application> {
|
||||
return await prisma.application.update({
|
||||
where: { id },
|
||||
@@ -286,6 +320,7 @@ export async function configureApplication({
|
||||
buildPack,
|
||||
fqdn,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@@ -293,7 +328,12 @@ export async function configureApplication({
|
||||
publishDirectory,
|
||||
pythonWSGI,
|
||||
pythonModule,
|
||||
pythonVariable
|
||||
pythonVariable,
|
||||
dockerFileLocation,
|
||||
denoMainFile,
|
||||
denoOptions,
|
||||
baseImage,
|
||||
baseBuildImage
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,11 +11,12 @@ import generator from 'generate-password';
|
||||
import forge from 'node-forge';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
|
||||
export function generatePassword(length = 24): string {
|
||||
export function generatePassword(length = 24, symbols = false): string {
|
||||
return generator.generate({
|
||||
length,
|
||||
numbers: true,
|
||||
strict: true
|
||||
strict: true,
|
||||
symbols
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ export function ErrorHandler(e: {
|
||||
truncatedError.message = 'git clone failed';
|
||||
}
|
||||
if (!e.message?.includes('Coolify Proxy is not running')) {
|
||||
sentry.captureException(truncatedError);
|
||||
// sentry.captureException(truncatedError);
|
||||
}
|
||||
const payload = {
|
||||
status: truncatedError.status || 500,
|
||||
@@ -154,6 +155,20 @@ export function generateDatabaseConfiguration(database: Database & { settings: D
|
||||
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: {
|
||||
POSTGRESQL_POSTGRES_PASSWORD: string;
|
||||
POSTGRESQL_USERNAME: string;
|
||||
POSTGRESQL_PASSWORD: string;
|
||||
POSTGRESQL_DATABASE: string;
|
||||
@@ -205,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,
|
||||
@@ -220,6 +249,7 @@ export function generateDatabaseConfiguration(database: Database & { settings: D
|
||||
return {
|
||||
privatePort: 5432,
|
||||
environmentVariables: {
|
||||
POSTGRESQL_POSTGRES_PASSWORD: rootUserPassword,
|
||||
POSTGRESQL_PASSWORD: dbUserPassword,
|
||||
POSTGRESQL_USERNAME: dbUser,
|
||||
POSTGRESQL_DATABASE: defaultDatabase
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
import { decrypt, encrypt } from '$lib/crypto';
|
||||
import type { Minio, Service } from '@prisma/client';
|
||||
import type { Minio, Prisma, Service } from '@prisma/client';
|
||||
import cuid from 'cuid';
|
||||
import { generatePassword } from '.';
|
||||
import { prisma } from './common';
|
||||
|
||||
const include: Prisma.ServiceInclude = {
|
||||
destinationDocker: true,
|
||||
persistentStorage: true,
|
||||
serviceSecret: true,
|
||||
minio: true,
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true,
|
||||
ghost: true,
|
||||
meiliSearch: true,
|
||||
umami: true,
|
||||
hasura: true,
|
||||
fider: true
|
||||
};
|
||||
export async function listServicesWithIncludes() {
|
||||
return await prisma.service.findMany({
|
||||
include,
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
}
|
||||
export async function listServices(teamId: string): Promise<Service[]> {
|
||||
if (teamId === '0') {
|
||||
return await prisma.service.findMany({ include: { teams: true } });
|
||||
@@ -27,16 +47,6 @@ export async function newService({
|
||||
|
||||
export async function getService({ id, teamId }: { id: string; teamId: string }): Promise<Service> {
|
||||
let body;
|
||||
const include = {
|
||||
destinationDocker: true,
|
||||
plausibleAnalytics: true,
|
||||
minio: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true,
|
||||
ghost: true,
|
||||
serviceSecret: true,
|
||||
meiliSearch: true
|
||||
};
|
||||
if (teamId === '0') {
|
||||
body = await prisma.service.findFirst({
|
||||
where: { id },
|
||||
@@ -49,6 +59,12 @@ export async function getService({ id, teamId }: { id: string; teamId: string })
|
||||
});
|
||||
}
|
||||
|
||||
if (body?.serviceSecret.length > 0) {
|
||||
body.serviceSecret = body.serviceSecret.map((s) => {
|
||||
s.value = decrypt(s.value);
|
||||
return s;
|
||||
});
|
||||
}
|
||||
if (body.plausibleAnalytics?.postgresqlPassword)
|
||||
body.plausibleAnalytics.postgresqlPassword = decrypt(
|
||||
body.plausibleAnalytics.postgresqlPassword
|
||||
@@ -75,15 +91,25 @@ export async function getService({ id, teamId }: { id: string; teamId: string })
|
||||
|
||||
if (body.meiliSearch?.masterKey) body.meiliSearch.masterKey = decrypt(body.meiliSearch.masterKey);
|
||||
|
||||
if (body?.serviceSecret.length > 0) {
|
||||
body.serviceSecret = body.serviceSecret.map((s) => {
|
||||
s.value = decrypt(s.value);
|
||||
return s;
|
||||
});
|
||||
}
|
||||
if (body.wordpress?.ftpPassword) {
|
||||
body.wordpress.ftpPassword = decrypt(body.wordpress.ftpPassword);
|
||||
}
|
||||
if (body.wordpress?.ftpPassword) body.wordpress.ftpPassword = decrypt(body.wordpress.ftpPassword);
|
||||
|
||||
if (body.umami?.postgresqlPassword)
|
||||
body.umami.postgresqlPassword = decrypt(body.umami.postgresqlPassword);
|
||||
if (body.umami?.umamiAdminPassword)
|
||||
body.umami.umamiAdminPassword = decrypt(body.umami.umamiAdminPassword);
|
||||
if (body.umami?.hashSalt) body.umami.hashSalt = decrypt(body.umami.hashSalt);
|
||||
|
||||
if (body.hasura?.postgresqlPassword)
|
||||
body.hasura.postgresqlPassword = decrypt(body.hasura.postgresqlPassword);
|
||||
if (body.hasura?.graphQLAdminPassword)
|
||||
body.hasura.graphQLAdminPassword = decrypt(body.hasura.graphQLAdminPassword);
|
||||
|
||||
if (body.fider?.postgresqlPassword)
|
||||
body.fider.postgresqlPassword = decrypt(body.fider.postgresqlPassword);
|
||||
if (body.fider?.jwtSecret) body.fider.jwtSecret = decrypt(body.fider.jwtSecret);
|
||||
if (body.fider?.emailSmtpPassword)
|
||||
body.fider.emailSmtpPassword = decrypt(body.fider.emailSmtpPassword);
|
||||
|
||||
const settings = await prisma.setting.findFirst();
|
||||
|
||||
return { ...body, settings };
|
||||
@@ -209,6 +235,65 @@ export async function configureServiceType({
|
||||
meiliSearch: { create: { masterKey } }
|
||||
}
|
||||
});
|
||||
} else if (type === 'umami') {
|
||||
const umamiAdminPassword = encrypt(generatePassword());
|
||||
const postgresqlUser = cuid();
|
||||
const postgresqlPassword = encrypt(generatePassword());
|
||||
const postgresqlDatabase = 'umami';
|
||||
const hashSalt = encrypt(generatePassword(64));
|
||||
await prisma.service.update({
|
||||
where: { id },
|
||||
data: {
|
||||
type,
|
||||
umami: {
|
||||
create: {
|
||||
umamiAdminPassword,
|
||||
postgresqlDatabase,
|
||||
postgresqlPassword,
|
||||
postgresqlUser,
|
||||
hashSalt
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (type === 'hasura') {
|
||||
const postgresqlUser = cuid();
|
||||
const postgresqlPassword = encrypt(generatePassword());
|
||||
const postgresqlDatabase = 'hasura';
|
||||
const graphQLAdminPassword = encrypt(generatePassword());
|
||||
await prisma.service.update({
|
||||
where: { id },
|
||||
data: {
|
||||
type,
|
||||
hasura: {
|
||||
create: {
|
||||
postgresqlDatabase,
|
||||
postgresqlPassword,
|
||||
postgresqlUser,
|
||||
graphQLAdminPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (type === 'fider') {
|
||||
const postgresqlUser = cuid();
|
||||
const postgresqlPassword = encrypt(generatePassword());
|
||||
const postgresqlDatabase = 'fider';
|
||||
const jwtSecret = encrypt(generatePassword(64, true));
|
||||
await prisma.service.update({
|
||||
where: { id },
|
||||
data: {
|
||||
type,
|
||||
fider: {
|
||||
create: {
|
||||
postgresqlDatabase,
|
||||
postgresqlPassword,
|
||||
postgresqlUser,
|
||||
jwtSecret
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,95 +327,105 @@ 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 updateLanguageToolService({
|
||||
export async function updateFiderService({
|
||||
id,
|
||||
fqdn,
|
||||
name
|
||||
name,
|
||||
exposePort,
|
||||
emailNoreply,
|
||||
emailMailgunApiKey,
|
||||
emailMailgunDomain,
|
||||
emailMailgunRegion,
|
||||
emailSmtpHost,
|
||||
emailSmtpPort,
|
||||
emailSmtpUser,
|
||||
emailSmtpPassword,
|
||||
emailSmtpEnableStartTls
|
||||
}: {
|
||||
id: string;
|
||||
fqdn: string;
|
||||
exposePort?: number;
|
||||
name: string;
|
||||
emailNoreply: string;
|
||||
emailMailgunApiKey: string;
|
||||
emailMailgunDomain: string;
|
||||
emailMailgunRegion: string;
|
||||
emailSmtpHost: string;
|
||||
emailSmtpPort: number;
|
||||
emailSmtpUser: string;
|
||||
emailSmtpPassword: string;
|
||||
emailSmtpEnableStartTls: boolean;
|
||||
}): Promise<Service> {
|
||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||
return await prisma.service.update({
|
||||
where: { id },
|
||||
data: {
|
||||
fqdn,
|
||||
name,
|
||||
exposePort,
|
||||
fider: {
|
||||
update: {
|
||||
emailNoreply,
|
||||
emailMailgunApiKey,
|
||||
emailMailgunDomain,
|
||||
emailMailgunRegion,
|
||||
emailSmtpHost,
|
||||
emailSmtpPort,
|
||||
emailSmtpUser,
|
||||
emailSmtpPassword,
|
||||
emailSmtpEnableStartTls
|
||||
}
|
||||
|
||||
export async function updateMeiliSearchService({
|
||||
id,
|
||||
fqdn,
|
||||
name
|
||||
}: {
|
||||
id: string;
|
||||
fqdn: string;
|
||||
name: string;
|
||||
}): Promise<Service> {
|
||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||
}
|
||||
|
||||
export async function updateVaultWardenService({
|
||||
id,
|
||||
fqdn,
|
||||
name
|
||||
}: {
|
||||
id: string;
|
||||
fqdn: string;
|
||||
name: string;
|
||||
}): Promise<Service> {
|
||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||
}
|
||||
|
||||
export async function updateVsCodeServer({
|
||||
id,
|
||||
fqdn,
|
||||
name
|
||||
}: {
|
||||
id: string;
|
||||
fqdn: string;
|
||||
name: string;
|
||||
}): Promise<Service> {
|
||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||
});
|
||||
}
|
||||
|
||||
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 } } }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -348,22 +443,28 @@ 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 } } }
|
||||
});
|
||||
}
|
||||
|
||||
export async function removeService({ id }: { id: string }): Promise<void> {
|
||||
await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.fider.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.ghost.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.umami.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.hasura.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.minio.deleteMany({ where: { serviceId: id } });
|
||||
await prisma.vscodeserver.deleteMany({ where: { serviceId: id } });
|
||||
|
||||
@@ -3,6 +3,34 @@ import { promises as fs } from 'fs';
|
||||
import { checkPnpm } from './buildPacks/common';
|
||||
import { saveBuildLog } from './common';
|
||||
|
||||
export async function buildCacheImageForLaravel(data, imageForBuild) {
|
||||
const { applicationId, tag, workdir, docker, buildId, debug, secrets, pullmergeRequestId } = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${imageForBuild}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
if (secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Dockerfile.push(`COPY *.json *.mix.js /app/`);
|
||||
Dockerfile.push(`COPY resources /app/resources`);
|
||||
Dockerfile.push(`RUN yarn install && yarn production`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
||||
await buildImage({ applicationId, tag, workdir, docker, buildId, isCache: true, debug });
|
||||
}
|
||||
|
||||
export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
const {
|
||||
applicationId,
|
||||
@@ -21,7 +49,7 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${imageForBuild}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
@@ -41,10 +69,11 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
||||
}
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
if (installCommand) {
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''}/package.json ./`);
|
||||
Dockerfile.push(`RUN ${installCommand}`);
|
||||
}
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
Dockerfile.push(`RUN ${buildCommand}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
||||
await buildImage({ applicationId, tag, workdir, docker, buildId, isCache: true, debug });
|
||||
@@ -65,11 +94,13 @@ export async function buildCacheImageWithCargo(data, imageForBuild) {
|
||||
} = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${imageForBuild} as planner-${applicationId}`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push('RUN cargo install cargo-chef');
|
||||
Dockerfile.push('COPY . .');
|
||||
Dockerfile.push('RUN cargo chef prepare --recipe-path recipe.json');
|
||||
Dockerfile.push(`FROM ${imageForBuild}`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push('RUN cargo install cargo-chef');
|
||||
Dockerfile.push(`COPY --from=planner-${applicationId} /app/recipe.json recipe.json`);
|
||||
@@ -85,7 +116,8 @@ export async function buildImage({
|
||||
docker,
|
||||
buildId,
|
||||
isCache = false,
|
||||
debug = false
|
||||
debug = false,
|
||||
dockerFileLocation = '/Dockerfile'
|
||||
}) {
|
||||
if (isCache) {
|
||||
await saveBuildLog({ line: `Building cache image started.`, buildId, applicationId });
|
||||
@@ -103,11 +135,12 @@ export async function buildImage({
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: workdir },
|
||||
{
|
||||
dockerfile: isCache ? 'Dockerfile-cache' : 'Dockerfile',
|
||||
dockerfile: isCache ? `${dockerFileLocation}-cache` : dockerFileLocation,
|
||||
t: `${applicationId}:${tag}${isCache ? '-cache' : ''}`
|
||||
}
|
||||
);
|
||||
await streamEvents({ stream, docker, buildId, applicationId, debug });
|
||||
await saveBuildLog({ line: `Building image successful!`, buildId, applicationId });
|
||||
}
|
||||
|
||||
export function dockerInstance({ destinationDocker }): { engine: Dockerode; network: string } {
|
||||
|
||||
@@ -6,6 +6,7 @@ import crypto from 'crypto';
|
||||
import { checkContainer, checkHAProxy } from '.';
|
||||
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
||||
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||
import { listServicesWithIncludes } from '$lib/database';
|
||||
|
||||
const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555';
|
||||
|
||||
@@ -94,6 +95,8 @@ backend {{domain}}
|
||||
{{/isHttps}}
|
||||
http-request add-header X-Forwarded-Host %[req.hdr(host),lower]
|
||||
server {{id}} {{id}}:{{port}}
|
||||
compression algo gzip
|
||||
compression type text/html text/css text/plain text/xml text/x-component text/javascript application/x-javascript application/javascript application/json application/manifest+json application/vnd.api+json application/xml application/xhtml+xml application/rss+xml application/atom+xml application/vnd.ms-fontobject application/x-font-ttf application/x-font-opentype application/x-font-truetype image/svg+xml image/x-icon image/vnd.microsoft.icon font/ttf font/eot font/otf font/opentype
|
||||
{{/isRunning}}
|
||||
{{/applications}}
|
||||
|
||||
@@ -110,6 +113,8 @@ backend {{domain}}
|
||||
{{/isHttps}}
|
||||
http-request add-header X-Forwarded-Host %[req.hdr(host),lower]
|
||||
server {{id}} {{id}}:{{port}}
|
||||
compression algo gzip
|
||||
compression type text/html text/css text/plain text/xml text/x-component text/javascript application/x-javascript application/javascript application/json application/manifest+json application/vnd.api+json application/xml application/xhtml+xml application/rss+xml application/atom+xml application/vnd.ms-fontobject application/x-font-ttf application/x-font-opentype application/x-font-truetype image/svg+xml image/x-icon image/vnd.microsoft.icon font/ttf font/eot font/otf font/opentype
|
||||
{{/isRunning}}
|
||||
{{/services}}
|
||||
|
||||
@@ -125,6 +130,8 @@ backend {{domain}}
|
||||
{{/isHttps}}
|
||||
http-request add-header X-Forwarded-Host %[req.hdr(host),lower]
|
||||
server {{id}} {{id}}:{{port}} check fall 10
|
||||
compression algo gzip
|
||||
compression type text/html text/css text/plain text/xml text/x-component text/javascript application/x-javascript application/javascript application/json application/manifest+json application/vnd.api+json application/xml application/xhtml+xml application/rss+xml application/atom+xml application/vnd.ms-fontobject application/x-font-ttf application/x-font-opentype application/x-font-truetype image/svg+xml image/x-icon image/vnd.microsoft.icon font/ttf font/eot font/otf font/opentype
|
||||
{{/coolify}}
|
||||
`;
|
||||
|
||||
@@ -208,17 +215,7 @@ export async function configureHAProxy(): Promise<void> {
|
||||
}
|
||||
}
|
||||
}
|
||||
const services = await db.prisma.service.findMany({
|
||||
include: {
|
||||
destinationDocker: true,
|
||||
minio: true,
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true,
|
||||
ghost: true,
|
||||
meiliSearch: true
|
||||
}
|
||||
});
|
||||
const services = await listServicesWithIncludes();
|
||||
|
||||
for (const service of services) {
|
||||
const { fqdn, id, type, destinationDocker, destinationDockerId, updatedAt } = service;
|
||||
|
||||
@@ -126,8 +126,8 @@ export async function startTcpProxy(
|
||||
const host = getEngine(engine);
|
||||
|
||||
const containerName = `haproxy-for-${publicPort}`;
|
||||
const found = await checkContainer(engine, containerName);
|
||||
const foundDependentContainer = await checkContainer(engine, id);
|
||||
const found = await checkContainer(engine, containerName, true);
|
||||
const foundDependentContainer = await checkContainer(engine, id, true);
|
||||
|
||||
try {
|
||||
if (foundDependentContainer && !found) {
|
||||
@@ -161,8 +161,8 @@ export async function startHttpProxy(
|
||||
const host = getEngine(engine);
|
||||
|
||||
const containerName = `haproxy-for-${publicPort}`;
|
||||
const found = await checkContainer(engine, containerName);
|
||||
const foundDependentContainer = await checkContainer(engine, id);
|
||||
const found = await checkContainer(engine, containerName, true);
|
||||
const foundDependentContainer = await checkContainer(engine, id, true);
|
||||
|
||||
try {
|
||||
if (foundDependentContainer && !found) {
|
||||
@@ -186,7 +186,7 @@ export async function startHttpProxy(
|
||||
|
||||
export async function startCoolifyProxy(engine: string): Promise<void> {
|
||||
const host = getEngine(engine);
|
||||
const found = await checkContainer(engine, 'coolify-haproxy');
|
||||
const found = await checkContainer(engine, 'coolify-haproxy', true);
|
||||
const { proxyPassword, proxyUser, id } = await db.listSettings();
|
||||
if (!found) {
|
||||
const { stdout: Config } = await asyncExecShell(
|
||||
@@ -201,7 +201,25 @@ export async function startCoolifyProxy(engine: string): Promise<void> {
|
||||
await configureNetworkCoolifyProxy(engine);
|
||||
}
|
||||
|
||||
export async function checkContainer(engine: string, container: string): Promise<boolean> {
|
||||
export async function isContainerExited(engine: string, containerName: string): Promise<boolean> {
|
||||
let isExited = false;
|
||||
const host = getEngine(engine);
|
||||
try {
|
||||
const { stdout } = await asyncExecShell(
|
||||
`DOCKER_HOST="${host}" docker inspect -f '{{.State.Status}}' ${containerName}`
|
||||
);
|
||||
if (stdout.trim() === 'exited') {
|
||||
isExited = true;
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
return isExited;
|
||||
}
|
||||
export async function checkContainer(
|
||||
engine: string,
|
||||
container: string,
|
||||
remove: boolean = false
|
||||
): Promise<boolean> {
|
||||
const host = getEngine(engine);
|
||||
let containerFound = false;
|
||||
|
||||
@@ -212,7 +230,10 @@ export async function checkContainer(engine: string, container: string): Promise
|
||||
const parsedStdout = JSON.parse(stdout);
|
||||
const status = parsedStdout.Status;
|
||||
const isRunning = status === 'running';
|
||||
if (status === 'exited' || status === 'created') {
|
||||
if (status === 'created') {
|
||||
await asyncExecShell(`DOCKER_HOST="${host}" docker rm ${container}`);
|
||||
}
|
||||
if (remove && status === 'exited') {
|
||||
await asyncExecShell(`DOCKER_HOST="${host}" docker rm ${container}`);
|
||||
}
|
||||
if (isRunning) {
|
||||
|
||||
4
src/lib/lang.json
Normal file
4
src/lib/lang.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"fr": "Français",
|
||||
"en": "English"
|
||||
}
|
||||
@@ -7,9 +7,13 @@ import fs from 'fs/promises';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||
import { promises as dns } from 'dns';
|
||||
import { listServicesWithIncludes } from '$lib/database';
|
||||
|
||||
export async function letsEncrypt(domain: string, id?: string, isCoolify = false): Promise<void> {
|
||||
try {
|
||||
const certbotImage =
|
||||
process.arch === 'x64' ? 'certbot/certbot' : 'certbot/certbot:arm64v8-latest';
|
||||
|
||||
const data = await db.prisma.setting.findFirst();
|
||||
const { minPort, maxPort } = data;
|
||||
|
||||
@@ -63,7 +67,7 @@ export async function letsEncrypt(domain: string, id?: string, isCoolify = false
|
||||
if (found) return;
|
||||
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${randomPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${randomPort} -d ${nakedDomain} -d ${wwwDomain} --expand --agree-tos --non-interactive --register-unsafely-without-email ${
|
||||
`DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${randomPort} -v "coolify-letsencrypt:/etc/letsencrypt" ${certbotImage} --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${randomPort} -d ${nakedDomain} -d ${wwwDomain} --expand --agree-tos --non-interactive --register-unsafely-without-email ${
|
||||
dev ? '--test-cert' : ''
|
||||
}`
|
||||
);
|
||||
@@ -83,7 +87,7 @@ export async function letsEncrypt(domain: string, id?: string, isCoolify = false
|
||||
}
|
||||
if (found) return;
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${randomPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${randomPort} -d ${domain} --expand --agree-tos --non-interactive --register-unsafely-without-email ${
|
||||
`DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${randomPort} -v "coolify-letsencrypt:/etc/letsencrypt" ${certbotImage} --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${randomPort} -d ${domain} --expand --agree-tos --non-interactive --register-unsafely-without-email ${
|
||||
dev ? '--test-cert' : ''
|
||||
}`
|
||||
);
|
||||
@@ -105,6 +109,7 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
include: { destinationDocker: true, settings: true },
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
const { fqdn, isDNSCheckEnabled } = await db.prisma.setting.findFirst();
|
||||
for (const application of applications) {
|
||||
try {
|
||||
if (application.fqdn && application.destinationDockerId) {
|
||||
@@ -142,19 +147,7 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
console.log(`Error during generateSSLCerts with ${application.fqdn}: ${error}`);
|
||||
}
|
||||
}
|
||||
const services = await db.prisma.service.findMany({
|
||||
include: {
|
||||
destinationDocker: true,
|
||||
minio: true,
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true,
|
||||
ghost: true,
|
||||
meiliSearch: true
|
||||
},
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
|
||||
const services = await listServicesWithIncludes();
|
||||
for (const service of services) {
|
||||
try {
|
||||
if (service.fqdn && service.destinationDockerId) {
|
||||
@@ -178,7 +171,6 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
console.log(`Error during generateSSLCerts with ${service.fqdn}: ${error}`);
|
||||
}
|
||||
}
|
||||
const { fqdn } = await db.prisma.setting.findFirst();
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
@@ -200,6 +192,7 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
file.endsWith('.pem') && certificates.push(file.replace(/\.pem$/, ''));
|
||||
}
|
||||
}
|
||||
if (isDNSCheckEnabled) {
|
||||
const resolver = new dns.Resolver({ timeout: 2000 });
|
||||
resolver.setServers(['8.8.8.8', '1.1.1.1']);
|
||||
let ipv4, ipv6;
|
||||
@@ -270,5 +263,57 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!dev) {
|
||||
for (const ssl of ssls) {
|
||||
if (
|
||||
certificates.includes(ssl.domain) ||
|
||||
certificates.includes(ssl.domain.replace('www.', ''))
|
||||
) {
|
||||
} else {
|
||||
console.log('Generating SSL for', ssl.domain);
|
||||
return await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const ssl of ssls) {
|
||||
if (
|
||||
certificates.includes(ssl.domain) ||
|
||||
certificates.includes(ssl.domain.replace('www.', ''))
|
||||
) {
|
||||
console.log(`Certificate for ${ssl.domain} already exists`);
|
||||
} else {
|
||||
console.log('Generating SSL for', ssl.domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function renewSSLCerts(): Promise<void> {
|
||||
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"`
|
||||
);
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
341
src/lib/locales/en.json
Normal file
341
src/lib/locales/en.json
Normal file
@@ -0,0 +1,341 @@
|
||||
{
|
||||
"layout": {
|
||||
"update_done": "Update completed.",
|
||||
"wait_new_version_startup": "Waiting for the new version to start...",
|
||||
"new_version": "New version reachable. Reloading...",
|
||||
"switch_to_a_different_team": "Switch to a different team...",
|
||||
"update_available": "Update available"
|
||||
},
|
||||
"error": {
|
||||
"you_can_find_your_way_back": "You can find your way back",
|
||||
"here": "here",
|
||||
"you_are_lost": "Ooops you are lost! But don't be afraid!"
|
||||
},
|
||||
"index": {
|
||||
"dashboard": "Dashboard",
|
||||
"applications": "Applications",
|
||||
"destinations": "Destinations",
|
||||
"git_sources": "Git Sources",
|
||||
"databases": "Databases",
|
||||
"services": "Services",
|
||||
"teams": "Teams",
|
||||
"not_implemented_yet": "Not implemented yet",
|
||||
"database": "Database",
|
||||
"settings": "Settings",
|
||||
"global_settings": "Global Settings",
|
||||
"secret": "Secret",
|
||||
"team": "Team",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"login": {
|
||||
"already_logged_in": "Already logged in...",
|
||||
"authenticating": "Authenticating...",
|
||||
"login": "Login"
|
||||
},
|
||||
"forms": {
|
||||
"password": "Password",
|
||||
"email": "Email address",
|
||||
"passwords_not_match": "Passwords do not match.",
|
||||
"password_again": "Password again",
|
||||
"save": "Save",
|
||||
"saving": "Saving...",
|
||||
"name": "Name",
|
||||
"value": "Value",
|
||||
"action": "Action",
|
||||
"is_required": "is required.",
|
||||
"add": "Add",
|
||||
"set": "Set",
|
||||
"remove": "Remove",
|
||||
"path": "Path",
|
||||
"confirm_continue": "Are you sure to continue?",
|
||||
"must_be_stopped_to_modify": "Must be stopped to modify.",
|
||||
"port": "Port",
|
||||
"default": "default",
|
||||
"base_directory": "Base Directory",
|
||||
"publish_directory": "Publish Directory",
|
||||
"generated_automatically_after_start": "Generated automatically after start",
|
||||
"roots_password": "Root's Password",
|
||||
"root_user": "Root User",
|
||||
"eg": "eg",
|
||||
"user": "User",
|
||||
"loading": "Loading...",
|
||||
"version": "Version",
|
||||
"host": "Host",
|
||||
"already_used_for": "<span class=\"text-red-500\">{{type}}</span> already used for",
|
||||
"configuration": "Configuration",
|
||||
"engine": "Engine",
|
||||
"network": "Network",
|
||||
"ip_address": "IP Address",
|
||||
"ssh_private_key": "SSH Private Key",
|
||||
"type": "Type",
|
||||
"html_url": "HTML URL",
|
||||
"api_url": "API URL",
|
||||
"organization": "Organization",
|
||||
"new_password": "New password",
|
||||
"super_secure_new_password": "Super secure new password",
|
||||
"submit": "Submit",
|
||||
"default_email_address": "Default Email Address",
|
||||
"default_password": "Default Password",
|
||||
"username": "Username",
|
||||
"root_db_user": "Root DB User",
|
||||
"root_db_password": "Root DB Password",
|
||||
"api_port": "API Port",
|
||||
"verifying": "Verifying",
|
||||
"verify_emails_without_smtp": "Verify emails without SMTP",
|
||||
"extra_config": "Extra Config",
|
||||
"select_a_service": "Select a Service",
|
||||
"select_a_service_version": "Select a Service version",
|
||||
"removing": "Removing...",
|
||||
"remove_domain": "Remove domain",
|
||||
"public_port_range": "Public Port Range",
|
||||
"public_port_range_explainer": "Ports used to expose databases/services/internal services.<br> Add them to your firewall (if applicable).<br><br>You can specify a range of ports, eg: <span class='text-yellow-500 font-bold'>9000-9100</span>",
|
||||
"no_actions_available": "No actions available",
|
||||
"admin_api_key": "Admin API key"
|
||||
},
|
||||
"register": {
|
||||
"register": "Register",
|
||||
"registering": "Registering...",
|
||||
"first_user": "You are registering the first user. It will be the administrator of your Coolify instance."
|
||||
},
|
||||
"reset": {
|
||||
"reset_password": "Reset",
|
||||
"invalid_secret_key": "Invalid secret key.",
|
||||
"secret_key": "Secret Key",
|
||||
"find_path_secret_key": "You can find it in ~/coolify/.env (COOLIFY_SECRET_KEY)"
|
||||
},
|
||||
"application": {
|
||||
"configuration": {
|
||||
"buildpack": {
|
||||
"choose_this_one": "Choose this one..."
|
||||
},
|
||||
"branch_already_in_use": "This branch is already used by another application. Webhooks won't work in this case for both applications. Are you sure you want to use it?",
|
||||
"no_repositories_configured": "No repositories configured for your Git Application.",
|
||||
"configure_it_now": "Configure it now",
|
||||
"loading_repositories": "Loading repositories ...",
|
||||
"select_a_repository": "Please select a repository",
|
||||
"loading_branches": "Loading branches ...",
|
||||
"select_a_repository_first": "Please select a repository first",
|
||||
"select_a_branch": "Please select a branch",
|
||||
"loading_groups": "Loading groups...",
|
||||
"select_a_group": "Please select a group",
|
||||
"loading_projects": "Loading projects...",
|
||||
"select_a_project": "Please select a project",
|
||||
"no_projects_found": "No projects found",
|
||||
"no_branches_found": "No branches found",
|
||||
"configure_build_pack": "Configure Build Pack",
|
||||
"scanning_repository_suggest_build_pack": "Scanning repository to suggest a build pack for you...",
|
||||
"found_lock_file": "Found lock file for <span class=\"font-bold text-orange-500 px-1\"> {{packageManager}}</span>.Using it for predefined commands commands.",
|
||||
"configure_destination": "Configure Destination",
|
||||
"no_configurable_destination": "No configurable Destination found",
|
||||
"select_a_repository_project": "Select a Repository / Project",
|
||||
"select_a_git_source": "Select a Git Source",
|
||||
"no_configurable_git": "No configurable Git Source found",
|
||||
"configuration_missing": "Configuration missing"
|
||||
},
|
||||
"build": {
|
||||
"queued_waiting_exec": "Queued and waiting for execution.",
|
||||
"build_logs_of": "Build logs of",
|
||||
"running": "Running",
|
||||
"queued": "Queued",
|
||||
"finished_in": "Finished in",
|
||||
"load_more": "Load More",
|
||||
"no_logs": "No logs found",
|
||||
"waiting_logs": "Waiting for the logs..."
|
||||
},
|
||||
"preview": {
|
||||
"need_during_buildtime": "Need during buildtime?",
|
||||
"setup_secret_app_first": "You can add secrets to PR/MR deployments. Please add secrets to the application first. <br>Useful for creating <span class='text-green-500 font-bold'>staging</span> environments.",
|
||||
"values_overwriting_app_secrets": "These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-green-500 font-bold'>staging</span> environments.",
|
||||
"redeploy": "Redeploy",
|
||||
"no_previews_available": "No previews available"
|
||||
},
|
||||
"secrets": {
|
||||
"secret_saved": "Secret saved.",
|
||||
"use_isbuildsecret": "Use isBuildSecret",
|
||||
"secrets_for": "Secrets for"
|
||||
},
|
||||
"storage": {
|
||||
"path_is_required": "Path is required.",
|
||||
"storage_saved": "Storage saved.",
|
||||
"storage_updated": "Storage updated.",
|
||||
"storage_deleted": "Storage deleted.",
|
||||
"persistent_storage_explainer": "You can specify any folder that you want to be persistent across deployments. <br>This is useful for storing data such as a database (SQLite) or a cache."
|
||||
},
|
||||
"deployment_queued": "Deployment queued.",
|
||||
"confirm_to_delete": "Are you sure you would like to delete '{{name}}'?",
|
||||
"stop_application": "Stop application",
|
||||
"permission_denied_stop_application": "You do not have permission to stop the application.",
|
||||
"rebuild_application": "Rebuild application",
|
||||
"permission_denied_rebuild_application": "You do not have permission to rebuild application.",
|
||||
"build_and_start_application": "Build and start application",
|
||||
"permission_denied_build_and_start_application": "You do not have permission to Build and start application.",
|
||||
"configurations": "Configurations",
|
||||
"secret": "Secrets",
|
||||
"persistent_storage": "Persistent Storage",
|
||||
"previews": "Previews",
|
||||
"logs": "Application Logs",
|
||||
"build_logs": "Build Logs",
|
||||
"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 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",
|
||||
"base_image": "Deployment Image",
|
||||
"base_image_explainer": "Image that will be used for the deployment.",
|
||||
"base_build_image": "Build Image",
|
||||
"base_build_image_explainer": "Image that will be used during the build process.",
|
||||
"destination": "Destination",
|
||||
"application": "Application",
|
||||
"url_fqdn": "URL (FQDN)",
|
||||
"domain_fqdn": "Domain (FQDN)",
|
||||
"https_explainer": "If you specify <span class='text-green-500 font-bold'>https</span>, the application will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-green-500 font-bold'>www</span>, the application will be redirected (302) from non-www and vice versa.<br><br>To modify the domain, you must first stop the application.<br><br><span class='text-white font-bold'>You must set your DNS to point to the server IP in advance.</span>",
|
||||
"ssl_www_and_non_www": "Generate SSL for www and non-www?",
|
||||
"ssl_explainer": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-green-500'>both DNS entries</span> set in advance.<br><br>Useful if you expect to have visitors on both.",
|
||||
"install_command": "Install Command",
|
||||
"build_command": "Build Command",
|
||||
"start_command": "Start Command",
|
||||
"directory_to_use_explainer": "Directory to use as the base for all commands.<br>Could be useful with <span class='text-green-500 font-bold'>monorepos</span>.",
|
||||
"publish_directory_explainer": "Directory containing all the assets for deployment. <br> For example: <span class='text-green-500 font-bold'>dist</span>,<span class='text-green-500 font-bold'>_site</span> or <span class='text-green-500 font-bold'>public</span>.",
|
||||
"features": "Features",
|
||||
"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.",
|
||||
"cant_activate_auto_deploy_without_repo": "Cannot activate automatic deployments until only one application is defined for this repository / branch.",
|
||||
"no_applications_found": "No applications found",
|
||||
"secret__batch_dot_env": "Paste .env file",
|
||||
"batch_secrets": "Batch add secrets"
|
||||
},
|
||||
"general": "General",
|
||||
"database": {
|
||||
"default_database": "Default Database",
|
||||
"generated_automatically_after_set_to_public": "Generated automatically after set to public",
|
||||
"connection_string": "Connection String",
|
||||
"set_public": "Set it public",
|
||||
"warning_database_public": "Your database will be reachable over the internet. <br>Take security seriously in this case!",
|
||||
"change_append_only_mode": "Change append only mode",
|
||||
"warning_append_only": "Useful if you would like to restore redis data from a backup.<br><span class='font-bold text-white'>Database restart is required.</span>",
|
||||
"select_database_type": "Select a Database type",
|
||||
"select_database_version": "Select a Database version",
|
||||
"confirm_stop": "Are you sure you would like to stop {{name}}?",
|
||||
"stop_database": "Stop database",
|
||||
"permission_denied_stop_database": "You do not have permission to stop the database.",
|
||||
"start_database": "Start database",
|
||||
"permission_denied_start_database": "You do not have permission to start the database.",
|
||||
"delete_database": "Delete Database",
|
||||
"permission_denied_delete_database": "You do not have permission to delete a Database",
|
||||
"no_databases_found": "No databases found",
|
||||
"logs": "Database Logs"
|
||||
},
|
||||
"destination": {
|
||||
"delete_destination": "Delete Destination",
|
||||
"permission_denied_delete_destination": "You do not have permission to delete this destination",
|
||||
"add_to_coolify": "Add to Coolify",
|
||||
"coolify_proxy_stopped": "Coolify Proxy stopped!",
|
||||
"coolify_proxy_started": "Coolify Proxy started!",
|
||||
"confirm_restart_proxy": "Are you sure you want to restart the proxy? Everything will be reconfigured in ~10 secs.",
|
||||
"coolify_proxy_restarting": "Coolify Proxy restarting...",
|
||||
"restarting_please_wait": "Restarting... please wait...",
|
||||
"force_restart_proxy": "Force restart proxy",
|
||||
"use_coolify_proxy": "Use Coolify Proxy?",
|
||||
"no_destination_found": "No destination found",
|
||||
"new_error_network_already_exists": "Network {{network}} already configured for another team!",
|
||||
"new": {
|
||||
"saving_and_configuring_proxy": "Saving and configuring proxy...",
|
||||
"install_proxy": "This will install a proxy on the destination to allow you to access your applications and services without any manual configuration (recommended for Docker).<br><br>Databases will have their own proxy.",
|
||||
"add_new_destination": "Add New Destination",
|
||||
"predefined_destinations": "Predefined destinations"
|
||||
}
|
||||
},
|
||||
"sources": {
|
||||
"local_docker": "Local Docker",
|
||||
"remote_docker": "Remote Docker",
|
||||
"organization_explainer": "Fill it if you would like to use an organization's as your Git Source. Otherwise your user will be used."
|
||||
},
|
||||
"source": {
|
||||
"new": {
|
||||
"git_source": "Add New Git Source",
|
||||
"official_providers": "Official providers"
|
||||
},
|
||||
"no_git_sources_found": "No git sources found",
|
||||
"delete_git_source": "Delete Git Source",
|
||||
"permission_denied": "You do not have permission to delete a Git Source",
|
||||
"create_new_app": "Create new {{name}} App",
|
||||
"change_app_settings": "Change {{name}} App Settings",
|
||||
"install_repositories": "Install Repositories",
|
||||
"application_id": "Application ID",
|
||||
"group_name": "Group Name",
|
||||
"oauth_id": "OAuth ID",
|
||||
"oauth_id_explainer": "The OAuth ID is the unique identifier of the GitLab application. <br>You can find it <span class='font-bold text-orange-600' >in the URL</span> of your GitLab OAuth Application.",
|
||||
"register_oauth_gitlab": "Register new OAuth application on GitLab",
|
||||
"gitlab": {
|
||||
"self_hosted": "Instance-wide application (self-hosted)",
|
||||
"user_owned": "User owned application",
|
||||
"group_owned": "Group owned application",
|
||||
"gitlab_application_type": "GitLab Application Type",
|
||||
"already_configured": "GitLab App is already configured."
|
||||
},
|
||||
"github": {
|
||||
"redirecting": "Redirecting to Github..."
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"all_email_verified": "All email verified. You can login now.",
|
||||
"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-pink-600'>both DNS entries</span> set in advance.<br><br>Service needs to be restarted."
|
||||
},
|
||||
"service": {
|
||||
"stop_service": "Stop Service",
|
||||
"permission_denied_stop_service": "You do not have permission to stop the service.",
|
||||
"start_service": "Start Service",
|
||||
"permission_denied_start_service": "You do not have permission to start the service.",
|
||||
"delete_service": "Delete Service",
|
||||
"permission_denied_delete_service": "You do not have permission to delete a service.",
|
||||
"no_service": "No services found",
|
||||
"logs": "Service Logs"
|
||||
},
|
||||
"setting": {
|
||||
"change_language": "Change Language",
|
||||
"permission_denied": "You do not have permission to do this. \\nAsk an admin to modify your permissions.",
|
||||
"domain_removed": "Domain removed",
|
||||
"ssl_explainer": "If you specify <span class='text-yellow-500 font-bold'>https</span>, Coolify will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-yellow-500 font-bold'>www</span>, Coolify will be redirected (302) from non-www and vice versa.<br><br><span class='text-yellow-500 font-bold'>WARNING:</span> If you change an already set domain, it will brake webhooks and other integrations! You need to manually update them.",
|
||||
"must_remove_domain_before_changing": "Must remove the domain before you can change this setting.",
|
||||
"registration_allowed": "Registration allowed?",
|
||||
"registration_allowed_explainer": "Allow further registrations to the application. <br>It's turned off after the first registration.",
|
||||
"coolify_proxy_settings": "Coolify Proxy Settings",
|
||||
"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.",
|
||||
"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."
|
||||
},
|
||||
"team": {
|
||||
"pending_invitations": "Pending invitations",
|
||||
"accept": "Accept",
|
||||
"delete": "Delete",
|
||||
"member": "member(s)",
|
||||
"root": "(root)",
|
||||
"invited_with_permissions": "Invited to <span class=\"font-bold text-pink-600\">{{teamName}}</span> with <span class=\"font-bold text-rose-600\">{{permission}}</span> permission.",
|
||||
"members": "Members",
|
||||
"root_team_explainer": "This is the <span class='text-red-500 font-bold'>root</span> team. That means members of this group can manage instance wide settings and have all the priviliges in Coolify (imagine like root user on Linux).",
|
||||
"permission": "Permission",
|
||||
"you": "(You)",
|
||||
"promote_to": "Promote to {{grade}}",
|
||||
"revoke_invitation": "Revoke invitation",
|
||||
"pending_invitation": "Pending invitation",
|
||||
"invite_new_member": "Invite new member",
|
||||
"send_invitation": "Send invitation",
|
||||
"invite_only_register_explainer": "You can only invite registered users at the moment - will be extended soon.",
|
||||
"admin": "Admin",
|
||||
"read": "Read"
|
||||
}
|
||||
}
|
||||
323
src/lib/locales/fr.json
Normal file
323
src/lib/locales/fr.json
Normal file
@@ -0,0 +1,323 @@
|
||||
{
|
||||
"application": {
|
||||
"application": "Application",
|
||||
"build": {
|
||||
"build_logs_of": "Créer des journaux de",
|
||||
"finished_in": "Fini en",
|
||||
"load_more": "Charger plus",
|
||||
"no_logs": "Aucun journal trouvé",
|
||||
"queued": "En file d'attente",
|
||||
"queued_waiting_exec": "En file d'attente et en attente d'exécution.",
|
||||
"running": "Fonctionnement",
|
||||
"waiting_logs": "En attente des logs..."
|
||||
},
|
||||
"build_and_start_application": "Build et démarrer l'application",
|
||||
"build_command": "Commande Build",
|
||||
"build_logs": "Créer des journaux",
|
||||
"build_pack": "Pack de Build",
|
||||
"cant_activate_auto_deploy_without_repo": "Impossible d'activer les déploiements automatiques tant qu'une seule application n'est pas définie pour ce dépôt/branche.",
|
||||
"configuration": {
|
||||
"branch_already_in_use": "Cette branche est déjà utilisée par une autre application. \nLes webhooks ne fonctionneront pas dans ce cas pour les deux applications. \nÊtes-vous sûr de vouloir l'utiliser ?",
|
||||
"buildpack": {
|
||||
"choose_this_one": "Choisir celui-ci..."
|
||||
},
|
||||
"configuration_missing": "Configuration manquante",
|
||||
"configure_build_pack": "Configurer le pack de build",
|
||||
"configure_destination": "Configurer la destination",
|
||||
"configure_it_now": "Configurez-le maintenant",
|
||||
"found_lock_file": "Fichier .lock trouvé pour <span class=\"font-bold text-orange-500 pl-1\">{{packageManager}}</span>. \nL'utiliser pour les commandes prédéfinies.",
|
||||
"loading_branches": "Chargement des branches...",
|
||||
"loading_groups": "Chargement des groupes...",
|
||||
"loading_projects": "Chargement des projets...",
|
||||
"loading_repositories": "Chargement des dépôts Git...",
|
||||
"no_branches_found": "Aucune branche trouvée",
|
||||
"no_configurable_destination": "Aucune destination configurable trouvée",
|
||||
"no_configurable_git": "Aucune source Git configurable trouvée",
|
||||
"no_projects_found": "Aucun projet trouvé",
|
||||
"no_repositories_configured": "Aucun dépôt Git configuré pour votre application.",
|
||||
"scanning_repository_suggest_build_pack": "Analyse du dépôt pour vous suggérer un pack de Build...",
|
||||
"select_a_branch": "Veuillez sélectionner une branche",
|
||||
"select_a_git_source": "Sélectionnez une source Git",
|
||||
"select_a_group": "Veuillez sélectionner un groupe",
|
||||
"select_a_project": "Veuillez sélectionner un projet",
|
||||
"select_a_repository": "Veuillez sélectionner un dépôt",
|
||||
"select_a_repository_first": "Veuillez d'abord sélectionner un dépôt",
|
||||
"select_a_repository_project": "Sélectionnez un dépôt / projet"
|
||||
},
|
||||
"configurations": "Configurations",
|
||||
"confirm_to_delete": "Voulez-vous vraiment supprimer '{{name}}'?",
|
||||
"debug_logs": "Journaux de débogage",
|
||||
"delete_application": "Supprimer l'application",
|
||||
"deployment_queued": "Déploiement en file d'attente.",
|
||||
"destination": "Destination",
|
||||
"directory_to_use_explainer": "Répertoire à utiliser comme base pour toutes les commandes.<br>Pourrait être utile avec <span class='text-green-500 font-bold'>monorepos</span>.",
|
||||
"dns_not_set_error": "DNS non défini ou propagé pour {{domain}}.<br><br>Veuillez vérifier vos paramètres DNS.",
|
||||
"dns_not_set_partial_error": "DNS non défini",
|
||||
"domain_already_in_use": "Le domaine {{domain}} est déjà utilisé.",
|
||||
"domain_fqdn": "Domaine (FQDN)",
|
||||
"url_fqdn": "URL (FQDN)",
|
||||
"enable_auto_deploy_webhooks": "Activez le déploiement automatique via des webhooks.",
|
||||
"enable_automatic_deployment": "Activer le déploiement automatique",
|
||||
"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",
|
||||
"https_explainer": "Si vous spécifiez <span class='text-green-500 font-bold'>https</span>, l'application sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-green-500 font-bold'>www</span>, l'application sera redirigée (302) à partir de non-www et vice versa \n.<br><br>Pour modifier le domaine, vous devez d'abord arrêter l'application.<br><br><span class='text-white font-bold'>Vous devez configurer, en avance, votre DNS pour pointer vers l'IP du serveur.</span>",
|
||||
"install_command": "Commande d'installation",
|
||||
"logs": "Journaux des applications",
|
||||
"no_applications_found": "Aucune application trouvée",
|
||||
"permission_denied_build_and_start_application": "Vous n'êtes pas autorisé à créer et à démarrer l'application.",
|
||||
"permission_denied_delete_application": "Vous n'êtes pas autorisé à supprimer cette application",
|
||||
"permission_denied_rebuild_application": "Vous n'êtes pas autorisé à re-build l'application.",
|
||||
"permission_denied_stop_application": "Vous n'êtes pas autorisé à arrêter l'application.",
|
||||
"persistent_storage": "Stockage persistant",
|
||||
"preview": {
|
||||
"need_during_buildtime": "Besoin pendant la build ?",
|
||||
"no_previews_available": "Aucun aperçu disponible",
|
||||
"redeploy": "Redéployer",
|
||||
"setup_secret_app_first": "Vous pouvez ajouter des secrets aux déploiements PR/MR. \nVeuillez d'abord ajouter des secrets à l'application. \n<br>Utile pour créer des environnements <span class='text-green-500 font-bold'>de mise en scène</span>.",
|
||||
"values_overwriting_app_secrets": "Ces valeurs remplacent les secrets d'application dans les déploiements PR/MR. \nUtile pour créer des environnements <span class='text-green-500 font-bold'>de mise en scène</span>."
|
||||
},
|
||||
"previews": "Aperçus",
|
||||
"publish_directory_explainer": "Répertoire contenant tous les actifs à déployer. \n<br> Par exemple : <span class='text-green-500 font-bold'>dist</span>,<span class='text-green-500 font-bold'>_site</span> ou <span \nclass='text-green-500 font-bold'>public</span>.",
|
||||
"rebuild_application": "Re-build l'application",
|
||||
"secret": "secrets",
|
||||
"secrets": {
|
||||
"secret_saved": "Secret enregistré.",
|
||||
"secrets_for": "secrets pour",
|
||||
"use_isbuildsecret": "Utiliser isBuildSecret"
|
||||
},
|
||||
"settings_saved": "Paramètres sauvegardés.",
|
||||
"ssl_explainer": "Il générera des certificats pour www et non-www. \n<br>Vous devez avoir <span class='font-bold text-green-500'>les deux entrées DNS</span> définies à l'avance.<br><br>Utile si vous prévoyez d'avoir des visiteurs sur les deux.",
|
||||
"ssl_www_and_non_www": "Générer SSL pour www et non-www ?",
|
||||
"start_command": "Démarrer la commande",
|
||||
"stop_application": "Arrêter l'application",
|
||||
"storage": {
|
||||
"path_is_required": "Le chemin est requis.",
|
||||
"persistent_storage_explainer": "Vous pouvez spécifier n'importe quel dossier que vous souhaitez conserver dans les déploiements. \n<br>Ceci est utile pour stocker des données telles qu'une base de données (SQLite) ou un cache.",
|
||||
"storage_deleted": "Stockage supprimé.",
|
||||
"storage_saved": "Stockage enregistré.",
|
||||
"storage_updated": "Stockage mis à jour."
|
||||
}
|
||||
},
|
||||
"database": {
|
||||
"change_append_only_mode": "Changer le mode d'ajout uniquement",
|
||||
"confirm_stop": "Êtes-vous sûr de vouloir arrêter {{name}} ?",
|
||||
"connection_string": "Connexion string",
|
||||
"default_database": "Base de données par défaut",
|
||||
"delete_database": "Supprimer la base de données",
|
||||
"generated_automatically_after_set_to_public": "Généré automatiquement après avoir été défini sur public",
|
||||
"no_databases_found": "Aucune base de données trouvée",
|
||||
"permission_denied_delete_database": "Vous n'êtes pas autorisé à supprimer une base de données",
|
||||
"permission_denied_start_database": "Vous n'êtes pas autorisé à démarrer la base de données.",
|
||||
"permission_denied_stop_database": "Vous n'êtes pas autorisé à arrêter la base de données.",
|
||||
"select_database_type": "Sélectionnez un type de base de données",
|
||||
"select_database_version": "Sélectionnez une version de la base de données",
|
||||
"set_public": "Rendre public",
|
||||
"start_database": "Démarrer la base de données",
|
||||
"stop_database": "Arrêter la base de données",
|
||||
"warning_append_only": "Utile si vous souhaitez restaurer des données Redis à partir d'une sauvegarde.<br><span class='font-bold text-white'>Le redémarrage de la base de données est nécessaire.</span>",
|
||||
"warning_database_public": "Votre base de données sera accessible depuis Internet. \n<br>Prenez la sécurité au sérieux dans ce cas!"
|
||||
},
|
||||
"destination": {
|
||||
"add_to_coolify": "Ajouter à Coolify",
|
||||
"confirm_restart_proxy": "Voulez-vous vraiment redémarrer le proxy? \nTout sera reconfiguré en ~10 secondes.",
|
||||
"coolify_proxy_restarting": "Redémarrage du Proxy Coolify...",
|
||||
"coolify_proxy_started": "Proxy Coolify démarré!",
|
||||
"coolify_proxy_stopped": "Proxy Coolify arrêté!",
|
||||
"delete_destination": "Supprimer le destinataire",
|
||||
"force_restart_proxy": "Forcer le redémarrage du proxy",
|
||||
"new": {
|
||||
"add_new_destination": "Ajouter une nouvelle destination",
|
||||
"install_proxy": "Cela installera un proxy sur la destination pour vous permettre d'accéder à vos applications et services sans aucune configuration manuelle (recommandé pour Docker).<br><br>Les bases de données auront leur propre proxy.",
|
||||
"predefined_destinations": "Destinations prédéfinies",
|
||||
"saving_and_configuring_proxy": "Enregistrement et configuration du proxy..."
|
||||
},
|
||||
"new_error_network_already_exists": "Réseau {{network}} déjà configuré pour une autre équipe !",
|
||||
"no_destination_found": "Aucune destination trouvée",
|
||||
"permission_denied_delete_destination": "Vous n'êtes pas autorisé à supprimer cette destination",
|
||||
"restarting_please_wait": "Redémarrage... veuillez patienter...",
|
||||
"use_coolify_proxy": "Utiliser le Proxy Coolify ?"
|
||||
},
|
||||
"error": {
|
||||
"here": "ici",
|
||||
"you_are_lost": "Oups vous êtes perdu ! \nMais n'ayez pas peur !",
|
||||
"you_can_find_your_way_back": "Tu peux retrouver ton chemin"
|
||||
},
|
||||
"forms": {
|
||||
"action": "action",
|
||||
"add": "Ajouter",
|
||||
"already_used_for": "<span class=\"text-red-500\">{{type}}</span> déjà utilisé pour",
|
||||
"api_port": "Port API",
|
||||
"api_url": "URL de l'API",
|
||||
"base_directory": "Répertoire de base",
|
||||
"configuration": "Configuration",
|
||||
"confirm_continue": "Êtes-vous sûr de continuer ?",
|
||||
"default": "défaut",
|
||||
"default_email_address": "Adresse e-mail par défaut",
|
||||
"default_password": "Mot de passe par défaut",
|
||||
"eg": "ex",
|
||||
"email": "Adresse e-mail",
|
||||
"engine": "Moteur",
|
||||
"extra_config": "Configuration supplémentaire",
|
||||
"generated_automatically_after_start": "Généré automatiquement après le démarrage",
|
||||
"host": "Hôte",
|
||||
"html_url": "URL HTML",
|
||||
"ip_address": "Adresse IP",
|
||||
"is_required": "est requis.",
|
||||
"loading": "Chargement...",
|
||||
"must_be_stopped_to_modify": "Doit être arrêté pour être modifié.",
|
||||
"name": "Nom",
|
||||
"network": "Réseau",
|
||||
"new_password": "Nouveau mot de passe",
|
||||
"no_actions_available": "Aucune action disponible",
|
||||
"organization": "Organisation",
|
||||
"password": "Mot de passe",
|
||||
"password_again": "Mot de passe à nouveau",
|
||||
"passwords_not_match": "Les mots de passe ne correspondent pas.",
|
||||
"path": "Chemin",
|
||||
"port": "Port",
|
||||
"public_port_range": "Gamme de ports publics",
|
||||
"public_port_range_explainer": "Ports utilisés pour exposer les bases de données/services/services internes.<br> Ajoutez-les à votre pare-feu (le cas échéant).<br><br>Vous pouvez spécifier une plage de ports, par exemple : <span class='text-yellow-500 \nfont-bold'>9000-9100</span>",
|
||||
"publish_directory": "Publier le répertoire",
|
||||
"remove": "Retirer",
|
||||
"remove_domain": "Supprimer le domaine",
|
||||
"removing": "Suppression...",
|
||||
"root_db_password": "Mot de passe root de la base de données",
|
||||
"root_db_user": "Utilisateur root de la base de données",
|
||||
"root_user": "Utilisateur root",
|
||||
"roots_password": "Mot de passe de l'utilisateur root",
|
||||
"save": "sauvegarder",
|
||||
"saving": "Sauvegarde...",
|
||||
"select_a_service": "Sélectionnez un service",
|
||||
"select_a_service_version": "Sélectionnez une version de service",
|
||||
"set": "Régler",
|
||||
"ssh_private_key": "Clé privée SSH",
|
||||
"submit": "Nous faire parvenir",
|
||||
"super_secure_new_password": "Nouveau mot de passe super sécurisé",
|
||||
"type": "Taper",
|
||||
"user": "Utilisateur",
|
||||
"username": "Nom d'utilisateur",
|
||||
"value": "Valeur",
|
||||
"verify_emails_without_smtp": "Vérifier les e-mails sans SMTP",
|
||||
"verifying": "Vérification",
|
||||
"version": "Version"
|
||||
},
|
||||
"general": "Général",
|
||||
"index": {
|
||||
"applications": "Applications",
|
||||
"dashboard": "Tableau de bord",
|
||||
"database": "Base de données",
|
||||
"databases": "Bases de données",
|
||||
"destinations": "Destinations",
|
||||
"git_sources": "Sources Git",
|
||||
"global_settings": "Paramètres globaux",
|
||||
"logout": "Se déconnecter",
|
||||
"not_implemented_yet": "Pas encore implémenté",
|
||||
"secret": "Secret",
|
||||
"services": "Services",
|
||||
"settings": "Réglages",
|
||||
"team": "Équipe",
|
||||
"teams": "Équipes"
|
||||
},
|
||||
"layout": {
|
||||
"new_version": "Nouvelle version accessible. \nRechargement...",
|
||||
"switch_to_a_different_team": "Changer d'équipe...",
|
||||
"update_available": "Mise à jour disponible",
|
||||
"update_done": "Mise à jour terminée.",
|
||||
"wait_new_version_startup": "En attendant le lancement de la nouvelle version..."
|
||||
},
|
||||
"login": {
|
||||
"already_logged_in": "Déjà connecté...",
|
||||
"authenticating": "Authentification...",
|
||||
"login": "Connexion"
|
||||
},
|
||||
"register": {
|
||||
"first_user": "Vous enregistrez le premier utilisateur. \nCe sera l'administrateur de votre instance Coolify.",
|
||||
"register": "S'inscrire"
|
||||
},
|
||||
"reset": {
|
||||
"find_path_secret_key": "Vous pouvez le trouver dans ~/coolify/.env (COOLIFY_SECRET_KEY)",
|
||||
"invalid_secret_key": "Clé secrète invalide.",
|
||||
"reset_password": "Réinitialiser",
|
||||
"secret_key": "Clef secrète"
|
||||
},
|
||||
"service": {
|
||||
"delete_service": "Supprimer le service",
|
||||
"no_service": "Aucun service trouvé",
|
||||
"permission_denied_delete_service": "Vous n'êtes pas autorisé à supprimer un service.",
|
||||
"permission_denied_start_service": "Vous n'êtes pas autorisé à démarrer le service.",
|
||||
"permission_denied_stop_service": "Vous n'êtes pas autorisé à arrêter le service.",
|
||||
"start_service": "Démarrer le service",
|
||||
"stop_service": "Stopper le service"
|
||||
},
|
||||
"services": {
|
||||
"all_email_verified": "Tous les e-mails sont vérifiés. \nVous pouvez vous connecter maintenant.",
|
||||
"generate_www_non_www_ssl": "Il générera des certificats pour www et non-www. \n<br>Vous devez avoir <span class='font-bold text-pink-600'>les deux entrées DNS</span> définies à l'avance.<br><br>Le service devra être redémarré."
|
||||
},
|
||||
"setting": {
|
||||
"coolify_proxy_settings": "Paramètres du proxy Coolify",
|
||||
"credential_stat_explainer": "Identifiants pour la page <a class=\"text-white font-bold\" href=\"{{link}}\" target=\"_blank\">statistiques</a>.",
|
||||
"domain_removed": "Domaine supprimé",
|
||||
"must_remove_domain_before_changing": "Vous devez supprimer le domaine avant de pouvoir modifier ce paramètre.",
|
||||
"permission_denied": "Vous n'avez pas la permission de faire cela. \n\\nDemandez à un administrateur de modifier vos autorisations.",
|
||||
"registration_allowed": "Inscription autorisée ?",
|
||||
"registration_allowed_explainer": "Autoriser d'autres inscriptions à l'application. \n<br>Il est désactivé après la première inscription.",
|
||||
"ssl_explainer": "Si vous spécifiez <span class='text-yellow-500 font-bold'>https</span>, Coolify sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-yellow-500 font-bold'>www</span>, Coolify sera redirigé (302) à partir de non-www et vice versa."
|
||||
},
|
||||
"source": {
|
||||
"application_id": "ID d'application",
|
||||
"change_app_settings": "Modifier les paramètres de l'application {{name}}",
|
||||
"create_new_app": "Créer une nouvelle application {{name}}",
|
||||
"delete_git_source": "Supprimer la source Git",
|
||||
"github": {
|
||||
"redirecting": "Redirection vers Github..."
|
||||
},
|
||||
"gitlab": {
|
||||
"already_configured": "L'application GitLab est déjà configurée.",
|
||||
"gitlab_application_type": "Type d'application GitLab",
|
||||
"group_owned": "Application détenue par le groupe",
|
||||
"self_hosted": "Application à l'échelle de l'instance (auto-hébergée)",
|
||||
"user_owned": "Application appartenant à l'utilisateur"
|
||||
},
|
||||
"group_name": "Nom de groupe",
|
||||
"install_repositories": "Installer les dépôts",
|
||||
"new": {
|
||||
"git_source": "Ajouter une nouvelle source Git",
|
||||
"official_providers": "Fournisseurs officiels"
|
||||
},
|
||||
"no_git_sources_found": "Aucune source git trouvée",
|
||||
"oauth_id": "ID OAuth",
|
||||
"oauth_id_explainer": "L'identifiant OAuth est l'identifiant unique de l'application GitLab. \n<br>Vous pouvez le trouver <span class='font-bold text-orange-600' >dans l'URL</span> de votre application GitLab OAuth.",
|
||||
"permission_denied": "Vous n'êtes pas autorisé à supprimer une source Git",
|
||||
"register_oauth_gitlab": "Enregistrer une nouvelle application OAuth sur GitLab"
|
||||
},
|
||||
"sources": {
|
||||
"local_docker": "Docker local",
|
||||
"organization_explainer": "Remplissez-le si vous souhaitez utiliser une organisation comme source Git. \nSinon, votre utilisateur sera utilisé.",
|
||||
"remote_docker": "Station d'accueil à distance"
|
||||
},
|
||||
"team": {
|
||||
"accept": "J'accepte",
|
||||
"admin": "Administrateur",
|
||||
"delete": "Supprimer",
|
||||
"invite_new_member": "Inviter un nouveau membre",
|
||||
"invite_only_register_explainer": "Vous ne pouvez inviter que des utilisateurs enregistrés pour le moment - sera bientôt prolongé.",
|
||||
"invited_with_permissions": "Invité à <span class=\"font-bold text-pink-600\">{{teamName}}</span> avec <span class=\"font-bold text-rose-600\">{{permission}}</span \n> autorisation.",
|
||||
"member": "membre(s)",
|
||||
"members": "Membres",
|
||||
"pending_invitation": "Invitation en attente",
|
||||
"pending_invitations": "Invitations en attente",
|
||||
"permission": "Autorisation",
|
||||
"promote_to": "Promouvoir à {{grade}}",
|
||||
"read": "Lire",
|
||||
"revoke_invitation": "Révoquer l'invitation",
|
||||
"root": "(suprême)",
|
||||
"root_team_explainer": "Il s'agit de l'équipe <span class='text-red-500 font-bold'>suprême</span>. \nCela signifie que les membres de ce groupe peuvent gérer les paramètres à l'échelle de l'instance et avoir tous les privilèges dans Coolify (imaginez comme un utilisateur root sous Linux).",
|
||||
"send_invitation": "Envoyer une invitation",
|
||||
"you": "(Toi)"
|
||||
}
|
||||
}
|
||||
42
src/lib/queues/autoUpdater.ts
Normal file
42
src/lib/queues/autoUpdater.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { prisma } from '$lib/database';
|
||||
import { buildQueue } from '.';
|
||||
import got from 'got';
|
||||
import { asyncExecShell, version } from '$lib/common';
|
||||
import compare from 'compare-versions';
|
||||
import { dev } from '$app/env';
|
||||
|
||||
export default async function (): Promise<void> {
|
||||
try {
|
||||
const currentVersion = version;
|
||||
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
|
||||
if (isAutoUpdateEnabled) {
|
||||
const versions = await got
|
||||
.get(
|
||||
`https://get.coollabs.io/versions.json?appId=${process.env['COOLIFY_APP_ID']}&version=${currentVersion}`
|
||||
)
|
||||
.json();
|
||||
const latestVersion = versions['coolify'].main.version;
|
||||
const isUpdateAvailable = compare(latestVersion, currentVersion);
|
||||
if (isUpdateAvailable === 1) {
|
||||
const activeCount = await buildQueue.getActiveCount();
|
||||
if (activeCount === 0) {
|
||||
if (!dev) {
|
||||
await buildQueue.pause();
|
||||
console.log(`Updating Coolify to ${latestVersion}.`);
|
||||
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
|
||||
await asyncExecShell(`env | grep COOLIFY > .env`);
|
||||
await asyncExecShell(
|
||||
`docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-redis && docker rm coolify coolify-redis && docker compose up -d --force-recreate"`
|
||||
);
|
||||
} else {
|
||||
await buildQueue.pause();
|
||||
console.log('Updating (not really in dev mode).');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
await buildQueue.resume();
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,11 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
persistentStorage,
|
||||
pythonWSGI,
|
||||
pythonModule,
|
||||
pythonVariable
|
||||
pythonVariable,
|
||||
denoOptions,
|
||||
exposePort,
|
||||
baseImage,
|
||||
baseBuildImage
|
||||
} = job.data;
|
||||
let {
|
||||
branch,
|
||||
@@ -56,7 +60,9 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
buildCommand,
|
||||
startCommand,
|
||||
baseDirectory,
|
||||
publishDirectory
|
||||
publishDirectory,
|
||||
dockerFileLocation,
|
||||
denoMainFile
|
||||
} = job.data;
|
||||
const { debug } = settings;
|
||||
|
||||
@@ -107,6 +113,8 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
buildCommand = configuration.buildCommand;
|
||||
publishDirectory = configuration.publishDirectory;
|
||||
baseDirectory = configuration.baseDirectory;
|
||||
dockerFileLocation = configuration.dockerFileLocation;
|
||||
denoMainFile = configuration.denoMainFile;
|
||||
|
||||
const commit = await importers[gitSource.type]({
|
||||
applicationId,
|
||||
@@ -145,6 +153,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
JSON.stringify({
|
||||
buildPack,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@@ -181,7 +190,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
//
|
||||
}
|
||||
if (!imageFound || deployNeeded) {
|
||||
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId);
|
||||
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage);
|
||||
if (buildpacks[buildPack])
|
||||
await buildpacks[buildPack]({
|
||||
buildId,
|
||||
@@ -200,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,
|
||||
@@ -209,7 +218,12 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
phpModules,
|
||||
pythonWSGI,
|
||||
pythonModule,
|
||||
pythonVariable
|
||||
pythonVariable,
|
||||
dockerFileLocation,
|
||||
denoMainFile,
|
||||
denoOptions,
|
||||
baseImage,
|
||||
baseBuildImage
|
||||
});
|
||||
else {
|
||||
await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId });
|
||||
@@ -251,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,
|
||||
@@ -286,6 +300,10 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
labels,
|
||||
depends_on: [],
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
// logging: {
|
||||
// driver: 'fluentd',
|
||||
// },
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
@@ -310,7 +328,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
await saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
|
||||
} catch (error) {
|
||||
await saveBuildLog({ line: error, buildId, applicationId });
|
||||
sentry.captureException(error);
|
||||
// sentry.captureException(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
|
||||
|
||||
@@ -4,7 +4,44 @@ export default async function (): Promise<void> {
|
||||
const destinationDockers = await prisma.destinationDocker.findMany();
|
||||
const engines = [...new Set(destinationDockers.map(({ engine }) => engine))];
|
||||
for (const engine of engines) {
|
||||
let lowDiskSpace = false;
|
||||
const host = getEngine(engine);
|
||||
try {
|
||||
const { stdout } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker exec coolify sh -c 'df -kPT /'`
|
||||
);
|
||||
let lines = stdout.trim().split('\n');
|
||||
let header = lines[0];
|
||||
let regex =
|
||||
/^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g;
|
||||
const boundaries = [];
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(header))) {
|
||||
boundaries.push(match[0].length);
|
||||
}
|
||||
|
||||
boundaries[boundaries.length - 1] = -1;
|
||||
const data = lines.slice(1).map((line) => {
|
||||
const cl = boundaries.map((boundary) => {
|
||||
const column = boundary > 0 ? line.slice(0, boundary) : line;
|
||||
line = line.slice(boundary);
|
||||
return column.trim();
|
||||
});
|
||||
return {
|
||||
capacity: Number.parseInt(cl[5], 10) / 100
|
||||
};
|
||||
});
|
||||
if (data.length > 0) {
|
||||
const { capacity } = data[0];
|
||||
if (capacity > 0.8) {
|
||||
lowDiskSpace = true;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
if (lowDiskSpace) {
|
||||
// Cleanup old coolify images
|
||||
try {
|
||||
let { stdout: images } = await asyncExecShell(
|
||||
@@ -35,3 +72,4 @@ export default async function (): Promise<void> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import proxy from './proxy';
|
||||
import proxyTcpHttp from './proxyTcpHttp';
|
||||
import ssl from './ssl';
|
||||
import sslrenewal from './sslrenewal';
|
||||
import autoUpdater from './autoUpdater';
|
||||
|
||||
import { asyncExecShell, saveBuildLog } from '$lib/common';
|
||||
|
||||
@@ -34,19 +35,22 @@ const cron = async (): Promise<void> => {
|
||||
new QueueScheduler('cleanup', connectionOptions);
|
||||
new QueueScheduler('ssl', connectionOptions);
|
||||
new QueueScheduler('sslRenew', connectionOptions);
|
||||
new QueueScheduler('autoUpdater', connectionOptions);
|
||||
|
||||
const queue = {
|
||||
proxy: new Queue('proxy', { ...connectionOptions }),
|
||||
proxyTcpHttp: new Queue('proxyTcpHttp', { ...connectionOptions }),
|
||||
cleanup: new Queue('cleanup', { ...connectionOptions }),
|
||||
ssl: new Queue('ssl', { ...connectionOptions }),
|
||||
sslRenew: new Queue('sslRenew', { ...connectionOptions })
|
||||
sslRenew: new Queue('sslRenew', { ...connectionOptions }),
|
||||
autoUpdater: new Queue('autoUpdater', { ...connectionOptions })
|
||||
};
|
||||
await queue.proxy.drain();
|
||||
await queue.proxyTcpHttp.drain();
|
||||
await queue.cleanup.drain();
|
||||
await queue.ssl.drain();
|
||||
await queue.sslRenew.drain();
|
||||
await queue.autoUpdater.drain();
|
||||
|
||||
new Worker(
|
||||
'proxy',
|
||||
@@ -98,11 +102,22 @@ const cron = async (): Promise<void> => {
|
||||
}
|
||||
);
|
||||
|
||||
new Worker(
|
||||
'autoUpdater',
|
||||
async () => {
|
||||
await autoUpdater();
|
||||
},
|
||||
{
|
||||
...connectionOptions
|
||||
}
|
||||
);
|
||||
|
||||
await queue.proxy.add('proxy', {}, { repeat: { every: 10000 } });
|
||||
await queue.proxyTcpHttp.add('proxyTcpHttp', {}, { repeat: { every: 10000 } });
|
||||
await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } });
|
||||
if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } });
|
||||
await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
|
||||
if (!dev) await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
|
||||
if (!dev) await queue.autoUpdater.add('autoUpdater', {}, { repeat: { every: 60000 } });
|
||||
};
|
||||
cron().catch((error) => {
|
||||
console.log('cron failed to start');
|
||||
@@ -115,6 +130,9 @@ const buildWorker = new Worker(buildQueueName, async (job) => await builder(job)
|
||||
concurrency: 1,
|
||||
...connectionOptions
|
||||
});
|
||||
buildQueue.resume().catch((err) => {
|
||||
console.log('Build queue failed to resume!', err);
|
||||
});
|
||||
|
||||
buildWorker.on('completed', async (job: Bullmq.Job) => {
|
||||
try {
|
||||
@@ -123,7 +141,6 @@ buildWorker.on('completed', async (job: Bullmq.Job) => {
|
||||
setTimeout(async () => {
|
||||
await prisma.build.update({ where: { id: job.data.build_id }, data: { status: 'success' } });
|
||||
}, 1234);
|
||||
console.log(error);
|
||||
} finally {
|
||||
const workdir = `/tmp/build-sources/${job.data.repository}/${job.data.build_id}`;
|
||||
if (!dev) await asyncExecShell(`rm -fr ${workdir}`);
|
||||
@@ -139,7 +156,6 @@ buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => {
|
||||
setTimeout(async () => {
|
||||
await prisma.build.update({ where: { id: job.data.build_id }, data: { status: 'failed' } });
|
||||
}, 1234);
|
||||
console.log(error);
|
||||
} finally {
|
||||
const workdir = `/tmp/build-sources/${job.data.repository}`;
|
||||
if (!dev) await asyncExecShell(`rm -fr ${workdir}`);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { asyncExecShell } from '$lib/common';
|
||||
import { reloadHaproxy } from '$lib/haproxy';
|
||||
import { renewSSLCerts } from '$lib/letsencrypt';
|
||||
|
||||
export default async function (): Promise<void> {
|
||||
await asyncExecShell(
|
||||
`docker run --rm --name certbot-renewal -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs renew`
|
||||
);
|
||||
await reloadHaproxy('unix:///var/run/docker.sock');
|
||||
try {
|
||||
return await renewSSLCerts();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { browser } from '$app/env';
|
||||
import { writable, type Writable, type Readable, readable } from 'svelte/store';
|
||||
|
||||
export const gitTokens: Writable<{ githubToken: string | null; gitlabToken: string | null }> =
|
||||
writable({
|
||||
githubToken: null,
|
||||
gitlabToken: null
|
||||
});
|
||||
export const disabledButton: Writable<boolean> = writable(false);
|
||||
|
||||
export const features: Readable<{ latestVersion: string; beta: boolean }> = readable({
|
||||
beta: browser && window.localStorage.getItem('beta') === 'true',
|
||||
latestVersion: browser && window.localStorage.getItem('latestVersion')
|
||||
});
|
||||
|
||||
25
src/lib/translations.ts
Normal file
25
src/lib/translations.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import i18n from 'sveltekit-i18n';
|
||||
import lang from './lang.json';
|
||||
|
||||
/** @type {import('sveltekit-i18n').Config} */
|
||||
export const config = {
|
||||
fallbackLocale: 'en',
|
||||
translations: {
|
||||
en: { lang },
|
||||
fr: { lang }
|
||||
},
|
||||
loaders: [
|
||||
{
|
||||
locale: 'en',
|
||||
key: '',
|
||||
loader: async () => (await import('./locales/en.json')).default
|
||||
},
|
||||
{
|
||||
locale: 'fr',
|
||||
key: '',
|
||||
loader: async () => (await import('./locales/fr.json')).default
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const { t, locales, locale, loadTranslations } = new i18n(config);
|
||||
@@ -12,6 +12,7 @@ export type BuilderJob = {
|
||||
buildPack: BuildPackName;
|
||||
projectId: number;
|
||||
port: number;
|
||||
exposePort?: number;
|
||||
installCommand: string;
|
||||
buildCommand?: string;
|
||||
startCommand?: string;
|
||||
@@ -21,6 +22,9 @@ export type BuilderJob = {
|
||||
pythonWSGI: string;
|
||||
pythonModule: string;
|
||||
pythonVariable: string;
|
||||
dockerFileLocation: string;
|
||||
denoMainFile: string;
|
||||
denoOptions: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
destinationDockerId: string;
|
||||
@@ -31,6 +35,8 @@ export type BuilderJob = {
|
||||
persistentStorage: { path: string }[];
|
||||
pullmergeRequestId?: unknown;
|
||||
sourceBranch?: string;
|
||||
baseImage: string;
|
||||
baseBuildImage: string;
|
||||
};
|
||||
|
||||
// TODO: Add the other build types
|
||||
|
||||
@@ -18,6 +18,7 @@ export type ComposeFileService = {
|
||||
restart: ComposeFileRestartOption;
|
||||
depends_on?: string[];
|
||||
command?: string;
|
||||
ports?: string[];
|
||||
build?: {
|
||||
context: string;
|
||||
dockerfile: string;
|
||||
|
||||
@@ -12,15 +12,18 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
export let status;
|
||||
export let error;
|
||||
</script>
|
||||
|
||||
<div class="mx-auto flex h-screen flex-col items-center justify-center px-4">
|
||||
<div class="pb-10 text-7xl font-bold">{status}</div>
|
||||
<div class="text-3xl font-bold">Ooops you are lost! But don't be afraid!</div>
|
||||
<div class="text-3xl font-bold">{$t('error.you_are_lost')}</div>
|
||||
<div class="text-xl">
|
||||
You can find your way back <a href="/" class="font-bold uppercase text-sky-400">here</a>
|
||||
{$t('error.you_can_find_your_way_back')}
|
||||
<a href="/" class="font-bold uppercase text-sky-400">{$t('error.here')}</a>
|
||||
</div>
|
||||
<div class="py-10 text-xs font-bold">
|
||||
<pre
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
import { publicPaths } from '$lib/settings';
|
||||
|
||||
import { locale, loadTranslations } from '$lib/translations';
|
||||
export const load: Load = async ({ fetch, url, session }) => {
|
||||
const { pathname } = url;
|
||||
|
||||
const defaultLocale = 'en';
|
||||
const sessionLocale = session.lang;
|
||||
const initLocale = sessionLocale || locale.get() || defaultLocale;
|
||||
await loadTranslations(initLocale, pathname);
|
||||
|
||||
if (!session.userId && !publicPaths.includes(url.pathname)) {
|
||||
return {
|
||||
status: 302,
|
||||
@@ -18,7 +25,6 @@
|
||||
if (res.ok) {
|
||||
return {
|
||||
props: {
|
||||
selectedTeamId: session.teamId,
|
||||
...(await res.json())
|
||||
}
|
||||
};
|
||||
@@ -28,9 +34,6 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export let teams;
|
||||
export let selectedTeamId;
|
||||
|
||||
import '../tailwind.css';
|
||||
import { SvelteToast, toast } from '@zerodevx/svelte-toast';
|
||||
import { page, session } from '$app/stores';
|
||||
@@ -38,8 +41,8 @@
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { asyncSleep } from '$lib/components/common';
|
||||
import { del, get, post } from '$lib/api';
|
||||
import { browser, dev } from '$app/env';
|
||||
|
||||
import { dev } from '$app/env';
|
||||
import { features } from '$lib/store';
|
||||
let isUpdateAvailable = false;
|
||||
|
||||
let updateStatus = {
|
||||
@@ -50,7 +53,7 @@
|
||||
let latestVersion = 'latest';
|
||||
onMount(async () => {
|
||||
if ($session.userId) {
|
||||
const overrideVersion = browser && window.localStorage.getItem('latestVersion');
|
||||
const overrideVersion = $features.latestVersion;
|
||||
try {
|
||||
await get(`/login.json`);
|
||||
} catch ({ error }) {
|
||||
@@ -83,17 +86,6 @@
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function switchTeam() {
|
||||
try {
|
||||
await post(`/dashboard.json?from=${$page.url.pathname}`, {
|
||||
cookie: 'teamId',
|
||||
value: selectedTeamId
|
||||
});
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
return window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
async function update() {
|
||||
updateStatus.loading = true;
|
||||
@@ -456,7 +448,6 @@
|
||||
<path d="M21 21v-2a4 4 0 0 0 -3 -3.85" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
{#if $session.teamId === '0'}
|
||||
<a
|
||||
sveltekit:prefetch
|
||||
@@ -524,17 +515,6 @@
|
||||
>Powered by <a href="https://coolify.io" target="_blank">Coolify</a></span
|
||||
>
|
||||
{/if}
|
||||
|
||||
<select
|
||||
class="fixed right-0 bottom-0 z-50 m-2 w-64 bg-opacity-30 p-2 px-4 hover:bg-opacity-100"
|
||||
bind:value={selectedTeamId}
|
||||
on:change={switchTeam}
|
||||
>
|
||||
<option value="" disabled selected>Switch to a different team...</option>
|
||||
{#each teams as team}
|
||||
<option value={team.teamId}>{team.team.name} - {team.permission}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
<main>
|
||||
<slot />
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
const endpoint = `/applications/${params.id}.json`;
|
||||
const res = await fetch(endpoint);
|
||||
if (res.ok) {
|
||||
let { application, isRunning, appId, githubToken, gitlabToken } = await res.json();
|
||||
let { application, isRunning, isExited, appId, githubToken, gitlabToken } = await res.json();
|
||||
if (!application || Object.entries(application).length === 0) {
|
||||
return {
|
||||
status: 302,
|
||||
@@ -46,6 +46,7 @@
|
||||
props: {
|
||||
application,
|
||||
isRunning,
|
||||
isExited,
|
||||
githubToken,
|
||||
gitlabToken
|
||||
},
|
||||
@@ -67,27 +68,39 @@
|
||||
<script lang="ts">
|
||||
export let application;
|
||||
export let isRunning;
|
||||
export let isExited;
|
||||
export let githubToken;
|
||||
export let gitlabToken;
|
||||
import { page, session } from '$app/stores';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
||||
import Loading from '$lib/components/Loading.svelte';
|
||||
import { del, post } from '$lib/api';
|
||||
import { del, get, post } from '$lib/api';
|
||||
import { goto } from '$app/navigation';
|
||||
import { gitTokens } from '$lib/store';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { disabledButton } from '$lib/store';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
if (githubToken) $gitTokens.githubToken = githubToken;
|
||||
if (gitlabToken) $gitTokens.gitlabToken = gitlabToken;
|
||||
|
||||
let loading = false;
|
||||
let statusInterval;
|
||||
$disabledButton =
|
||||
!$session.isAdmin ||
|
||||
!application.fqdn ||
|
||||
!application.gitSource ||
|
||||
!application.repository ||
|
||||
!application.destinationDocker ||
|
||||
!application.buildPack;
|
||||
const { id } = $page.params;
|
||||
|
||||
async function handleDeploySubmit() {
|
||||
try {
|
||||
const { buildId } = await post(`/applications/${id}/deploy.json`, { ...application });
|
||||
toast.push('Deployment queued.');
|
||||
toast.push($t('application.deployment_queued'));
|
||||
if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
|
||||
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
|
||||
} else {
|
||||
@@ -101,7 +114,7 @@
|
||||
}
|
||||
|
||||
async function deleteApplication(name) {
|
||||
const sure = confirm(`Are you sure you would like to delete '${name}'?`);
|
||||
const sure = confirm($t('application.confirm_to_delete', { name }));
|
||||
if (sure) {
|
||||
loading = true;
|
||||
try {
|
||||
@@ -121,23 +134,61 @@
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function getStatus() {
|
||||
statusInterval = setInterval(async () => {
|
||||
const data = await get(`/applications/${id}.json`);
|
||||
isRunning = data.isRunning;
|
||||
isExited = data.isExited;
|
||||
}, 1000);
|
||||
}
|
||||
onDestroy(() => {
|
||||
clearInterval(statusInterval);
|
||||
});
|
||||
onMount(async () => {
|
||||
await getStatus();
|
||||
});
|
||||
</script>
|
||||
|
||||
<nav class="nav-side">
|
||||
{#if loading}
|
||||
<Loading fullscreen cover />
|
||||
{:else}
|
||||
{#if application.fqdn && application.gitSource && application.repository && application.destinationDocker && application.buildPack}
|
||||
{#if isExited}
|
||||
<a
|
||||
href={!$disabledButton ? `/applications/${id}/logs` : null}
|
||||
class=" icons bg-transparent tooltip-bottom text-sm flex items-center text-red-500 tooltip-red-500"
|
||||
data-tooltip="Application exited with an error!"
|
||||
sveltekit:prefetch
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentcolor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M8.7 3h6.6c.3 0 .5 .1 .7 .3l4.7 4.7c.2 .2 .3 .4 .3 .7v6.6c0 .3 -.1 .5 -.3 .7l-4.7 4.7c-.2 .2 -.4 .3 -.7 .3h-6.6c-.3 0 -.5 -.1 -.7 -.3l-4.7 -4.7c-.2 -.2 -.3 -.4 -.3 -.7v-6.6c0 -.3 .1 -.5 .3 -.7l4.7 -4.7c.2 -.2 .4 -.3 .7 -.3z"
|
||||
/>
|
||||
<line x1="12" y1="8" x2="12" y2="12" />
|
||||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||||
</svg>
|
||||
</a>
|
||||
{/if}
|
||||
{#if isRunning}
|
||||
<button
|
||||
on:click={stopApplication}
|
||||
title="Stop application"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-red-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Stop application'
|
||||
: 'You do not have permission to stop the application.'}
|
||||
? $t('application.stop_application')
|
||||
: $t('application.permission_denied_stop_application')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -158,7 +209,7 @@
|
||||
<button
|
||||
title="Rebuild application"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:text-green-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Rebuild application'
|
||||
@@ -187,7 +238,7 @@
|
||||
<button
|
||||
title="Build and start application"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-green-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Build and start application'
|
||||
@@ -210,9 +261,9 @@
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
<div class="border border-stone-700 h-8" />
|
||||
<div class="border border-coolgray-500 h-8" />
|
||||
<a
|
||||
href="/applications/{id}"
|
||||
href={!$disabledButton ? `/applications/${id}` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-yellow-500 rounded"
|
||||
class:text-yellow-500={$page.url.pathname === `/applications/${id}`}
|
||||
@@ -220,7 +271,8 @@
|
||||
>
|
||||
<button
|
||||
title="Configurations"
|
||||
class="icons bg-transparent tooltip-bottom text-sm disabled:text-red-500"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
data-tooltip="Configurations"
|
||||
>
|
||||
<svg
|
||||
@@ -247,7 +299,7 @@
|
||||
></a
|
||||
>
|
||||
<a
|
||||
href="/applications/{id}/secrets"
|
||||
href={!$disabledButton ? `/applications/${id}/secrets` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-pink-500 rounded"
|
||||
class:text-pink-500={$page.url.pathname === `/applications/${id}/secrets`}
|
||||
@@ -255,7 +307,8 @@
|
||||
>
|
||||
<button
|
||||
title="Secret"
|
||||
class="icons bg-transparent tooltip-bottom text-sm disabled:text-red-500"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
data-tooltip="Secret"
|
||||
>
|
||||
<svg
|
||||
@@ -278,7 +331,7 @@
|
||||
></a
|
||||
>
|
||||
<a
|
||||
href="/applications/{id}/storage"
|
||||
href={!$disabledButton ? `/applications/${id}/storage` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-pink-500 rounded"
|
||||
class:text-pink-500={$page.url.pathname === `/applications/${id}/storage`}
|
||||
@@ -286,7 +339,8 @@
|
||||
>
|
||||
<button
|
||||
title="Persistent Storage"
|
||||
class="icons bg-transparent tooltip-bottom text-sm disabled:text-red-500"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
data-tooltip="Persistent Storage"
|
||||
>
|
||||
<svg
|
||||
@@ -307,7 +361,7 @@
|
||||
</button></a
|
||||
>
|
||||
<a
|
||||
href="/applications/{id}/previews"
|
||||
href={!$disabledButton ? `/applications/${id}/previews` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-orange-500 rounded"
|
||||
class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`}
|
||||
@@ -315,7 +369,8 @@
|
||||
>
|
||||
<button
|
||||
title="Previews"
|
||||
class="icons bg-transparent tooltip-bottom text-sm disabled:text-red-500"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
data-tooltip="Previews"
|
||||
>
|
||||
<svg
|
||||
@@ -337,18 +392,19 @@
|
||||
</svg></button
|
||||
></a
|
||||
>
|
||||
<div class="border border-stone-700 h-8" />
|
||||
<div class="border border-coolgray-500 h-8" />
|
||||
<a
|
||||
href="/applications/{id}/logs"
|
||||
href={!$disabledButton && isRunning ? `/applications/${id}/logs` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-sky-500 rounded"
|
||||
class:text-sky-500={$page.url.pathname === `/applications/${id}/logs`}
|
||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs`}
|
||||
>
|
||||
<button
|
||||
title="Application Logs"
|
||||
class="icons bg-transparent tooltip-bottom text-sm disabled:text-red-500 "
|
||||
data-tooltip="Application Logs"
|
||||
title={$t('application.logs')}
|
||||
disabled={$disabledButton || !isRunning}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
data-tooltip={$t('application.logs')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -370,7 +426,7 @@
|
||||
</button></a
|
||||
>
|
||||
<a
|
||||
href="/applications/{id}/logs/build"
|
||||
href={!$disabledButton ? `/applications/${id}/logs/build` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-red-500 rounded"
|
||||
class:text-red-500={$page.url.pathname === `/applications/${id}/logs/build`}
|
||||
@@ -378,7 +434,8 @@
|
||||
>
|
||||
<button
|
||||
title="Build Logs"
|
||||
class="icons bg-transparent tooltip-bottom text-sm disabled:text-red-500 "
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
data-tooltip="Build Logs"
|
||||
>
|
||||
<svg
|
||||
@@ -403,19 +460,18 @@
|
||||
</svg>
|
||||
</button></a
|
||||
>
|
||||
<div class="border border-stone-700 h-8" />
|
||||
{/if}
|
||||
<div class="border border-coolgray-500 h-8" />
|
||||
|
||||
<button
|
||||
on:click={() => deleteApplication(application.name)}
|
||||
title="Delete application"
|
||||
title={$t('application.delete_application')}
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
class:hover:text-red-500={$session.isAdmin}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Delete application'
|
||||
: 'You do not have permission to delete this application'}
|
||||
? $t('application.delete_application')
|
||||
: $t('application.permission_denied_delete_application')}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</button>
|
||||
|
||||
87
src/routes/applications/[id]/cancel.json.ts
Normal file
87
src/routes/applications/[id]/cancel.json.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { asyncExecShell, getEngine, removeDestinationDocker, saveBuildLog } from '$lib/common';
|
||||
import { buildQueue } from '$lib/queues';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import * as db from '$lib/database';
|
||||
|
||||
async function cleanupDB(buildId: string) {
|
||||
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' } });
|
||||
}
|
||||
}
|
||||
|
||||
async function stopBuild(buildId, applicationId) {
|
||||
let count = 0;
|
||||
await new Promise<void>(async (resolve, reject) => {
|
||||
const job = await buildQueue.getJob(buildId);
|
||||
if (!job) {
|
||||
await cleanupDB(buildId);
|
||||
return resolve();
|
||||
}
|
||||
const {
|
||||
destinationDocker: { engine }
|
||||
} = job?.data;
|
||||
const host = getEngine(engine);
|
||||
let interval = setInterval(async () => {
|
||||
try {
|
||||
const data = await db.prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (data?.status === 'failed') {
|
||||
clearInterval(interval);
|
||||
return resolve();
|
||||
}
|
||||
if (count > 100) {
|
||||
clearInterval(interval);
|
||||
return reject(new Error('Build canceled'));
|
||||
}
|
||||
|
||||
const { stdout: buildContainers } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker container ls --filter "label=coolify.buildId=${buildId}" --format '{{json .}}'`
|
||||
);
|
||||
if (buildContainers) {
|
||||
const containersArray = buildContainers.trim().split('\n');
|
||||
for (const container of containersArray) {
|
||||
const containerObj = JSON.parse(container);
|
||||
const id = containerObj.ID;
|
||||
if (!containerObj.Names.startsWith(`${applicationId}`)) {
|
||||
await removeDestinationDocker({ id, engine });
|
||||
clearInterval(interval);
|
||||
await saveBuildLog({
|
||||
line: 'Canceled by user!',
|
||||
buildId: job.data.build_id,
|
||||
applicationId: job.data.id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
count++;
|
||||
} catch (error) {}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { buildId, applicationId } = await event.request.json();
|
||||
if (!buildId) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
message: 'Build ID not found.'
|
||||
}
|
||||
};
|
||||
}
|
||||
try {
|
||||
await stopBuild(buildId, applicationId);
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
message: 'Build canceled.'
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
message: error.message
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,45 +1,68 @@
|
||||
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 {
|
||||
message: `Domain ${getDomain(fqdn).replace('www.', '')} is already used.`
|
||||
message: t.get('application.domain_already_in_use', {
|
||||
domain: getDomain(fqdn).replace('www.', '')
|
||||
})
|
||||
};
|
||||
}
|
||||
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: `DNS not set or propogated for ${domain}.<br><br>Please check your DNS settings.`
|
||||
};
|
||||
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 {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { post } from '$lib/api';
|
||||
import { findBuildPack } from '$lib/components/templates';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
const { id } = $page.params;
|
||||
const from = $page.url.searchParams.get('from');
|
||||
@@ -19,13 +20,14 @@
|
||||
const tempBuildPack = JSON.parse(
|
||||
JSON.stringify(findBuildPack(buildPack.name, packageManager))
|
||||
);
|
||||
|
||||
delete tempBuildPack.name;
|
||||
delete tempBuildPack.fancyName;
|
||||
delete tempBuildPack.color;
|
||||
delete tempBuildPack.hoverColor;
|
||||
|
||||
if (foundConfig.buildPack !== name) {
|
||||
await post(`/applications/${id}.json`, { ...tempBuildPack });
|
||||
await post(`/applications/${id}.json`, { ...tempBuildPack, buildPack: name });
|
||||
}
|
||||
await post(`/applications/${id}/configuration/buildpack.json`, { buildPack: name });
|
||||
return await goto(from || `/applications/${id}`);
|
||||
@@ -42,7 +44,9 @@
|
||||
buildPack.name && buildPack.color}"
|
||||
><span>{buildPack.fancyName}</span>
|
||||
{#if !scanning && foundConfig?.name === buildPack.name}
|
||||
<span class="absolute bottom-0 pb-2 text-xs">Choose this one...</span>
|
||||
<span class="absolute bottom-0 pb-2 text-xs"
|
||||
>{$t('application.configuration.buildpack.choose_this_one')}</span
|
||||
>
|
||||
{/if}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { onMount } from 'svelte';
|
||||
import { gitTokens } from '$lib/store';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
const { id } = $page.params;
|
||||
const from = $page.url.searchParams.get('from');
|
||||
@@ -95,9 +96,7 @@
|
||||
`/applications/${id}/configuration/repository.json?repository=${selected.repository}&branch=${selected.branch}`
|
||||
);
|
||||
if (data.used) {
|
||||
const sure = confirm(
|
||||
`This branch is already used by another application. Webhooks won't work in this case for both applications. Are you sure you want to use it?`
|
||||
);
|
||||
const sure = confirm($t('application.configuration.branch_already_in_use'));
|
||||
if (sure) {
|
||||
selected.autodeploy = false;
|
||||
showSave = true;
|
||||
@@ -171,8 +170,10 @@
|
||||
|
||||
{#if repositories.length === 0 && loading.repositories === false}
|
||||
<div class="flex-col text-center">
|
||||
<div class="pb-4">No repositories configured for your Git Application.</div>
|
||||
<a href={`/sources/${application.gitSource.id}`}><button>Configure it now</button></a>
|
||||
<div class="pb-4">{$t('application.configuration.no_repositories_configured')}</div>
|
||||
<a href={`/sources/${application.gitSource.id}`}
|
||||
><button>{$t('application.configuration.configure_it_now')}</button></a
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<form on:submit|preventDefault={handleSubmit} class="flex flex-col justify-center text-center">
|
||||
@@ -181,10 +182,10 @@
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
placeholder={loading.repositories
|
||||
? 'Loading repositories...'
|
||||
: 'Please select a repository'}
|
||||
? $t('application.configuration.loading_repositories')
|
||||
: $t('application.configuration.select_a_repository')}
|
||||
id="repository"
|
||||
showIndicator={true}
|
||||
showIndicator={!loading.repositories}
|
||||
isWaiting={loading.repositories}
|
||||
on:select={loadBranches}
|
||||
items={reposSelectOptions}
|
||||
@@ -196,12 +197,12 @@
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
placeholder={loading.branches
|
||||
? 'Loading branches...'
|
||||
? $t('application.configuration.loading_branches')
|
||||
: !selected.repository
|
||||
? 'Please select a repository first'
|
||||
: 'Please select a branch'}
|
||||
? $t('application.configuration.select_a_repository_first')
|
||||
: $t('application.configuration.select_a_branch')}
|
||||
isWaiting={loading.branches}
|
||||
showIndicator={selected.repository}
|
||||
showIndicator={selected.repository && !loading.branches}
|
||||
id="branches"
|
||||
on:select={isBranchAlreadyUsed}
|
||||
items={branchSelectOptions}
|
||||
@@ -217,7 +218,7 @@
|
||||
type="submit"
|
||||
disabled={!showSave}
|
||||
class:bg-orange-600={showSave}
|
||||
class:hover:bg-orange-500={showSave}>Save</button
|
||||
class:hover:bg-orange-500={showSave}>{$t('forms.save')}</button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { del, get, post, put } from '$lib/api';
|
||||
import { gitTokens } from '$lib/store';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
const { id } = $page.params;
|
||||
const from = $page.url.searchParams.get('from');
|
||||
@@ -139,9 +140,7 @@
|
||||
`/applications/${id}/configuration/repository.json?repository=${selected.project.path_with_namespace}&branch=${selected.branch.name}`
|
||||
);
|
||||
if (data.used) {
|
||||
const sure = confirm(
|
||||
`This branch is already used by another application. Webhooks won't work in this case for both applications. Are you sure you want to use it?`
|
||||
);
|
||||
const sure = confirm($t('application.configuration.branch_already_in_use'));
|
||||
if (sure) {
|
||||
autodeploy = false;
|
||||
showSave = true;
|
||||
@@ -270,11 +269,11 @@
|
||||
<div class="flex flex-col space-y-2 px-4 xl:flex-row xl:space-y-0 xl:space-x-2 ">
|
||||
{#if loading.base}
|
||||
<select name="group" disabled class="w-96">
|
||||
<option selected value="">Loading groups...</option>
|
||||
<option selected value="">{$t('application.configuration.loading_groups')}</option>
|
||||
</select>
|
||||
{:else}
|
||||
<select name="group" class="w-96" bind:value={selected.group} on:change={loadProjects}>
|
||||
<option value="" disabled selected>Please select a group</option>
|
||||
<option value="" disabled selected>{$t('application.configuration.select_a_group')}</option>
|
||||
{#each groups as group}
|
||||
<option value={group}>{group.full_name}</option>
|
||||
{/each}
|
||||
@@ -282,7 +281,7 @@
|
||||
{/if}
|
||||
{#if loading.projects}
|
||||
<select name="project" disabled class="w-96">
|
||||
<option selected value="">Loading projects...</option>
|
||||
<option selected value="">{$t('application.configuration.loading_projects')}</option>
|
||||
</select>
|
||||
{:else if !loading.projects && projects.length > 0}
|
||||
<select
|
||||
@@ -292,20 +291,24 @@
|
||||
on:change={loadBranches}
|
||||
disabled={!selected.group}
|
||||
>
|
||||
<option value="" disabled selected>Please select a project</option>
|
||||
<option value="" disabled selected
|
||||
>{$t('application.configuration.select_a_project')}</option
|
||||
>
|
||||
{#each projects as project}
|
||||
<option value={project}>{project.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<select name="project" disabled class="w-96">
|
||||
<option disabled selected value="">No projects found</option>
|
||||
<option disabled selected value=""
|
||||
>{$t('application.configuration.no_projects_found')}</option
|
||||
>
|
||||
</select>
|
||||
{/if}
|
||||
|
||||
{#if loading.branches}
|
||||
<select name="branch" disabled class="w-96">
|
||||
<option selected value="">Loading branches...</option>
|
||||
<option selected value="">{$t('application.configuration.loading_branches')}</option>
|
||||
</select>
|
||||
{:else if !loading.branches && branches.length > 0}
|
||||
<select
|
||||
@@ -315,14 +318,17 @@
|
||||
on:change={isBranchAlreadyUsed}
|
||||
disabled={!selected.project}
|
||||
>
|
||||
<option value="" disabled selected>Please select a branch</option>
|
||||
<option value="" disabled selected>{$t('application.configuration.select_a_branch')}</option
|
||||
>
|
||||
{#each branches as branch}
|
||||
<option value={branch}>{branch.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<select name="project" disabled class="w-96">
|
||||
<option disabled selected value="">No branches found</option>
|
||||
<option disabled selected value=""
|
||||
>{$t('application.configuration.no_branches_found')}</option
|
||||
>
|
||||
</select>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -334,7 +340,7 @@
|
||||
disabled={!showSave || loading.save}
|
||||
class:bg-orange-600={showSave && !loading.save}
|
||||
class:hover:bg-orange-500={showSave && !loading.save}
|
||||
>{loading.save ? 'Saving...' : 'Save'}</button
|
||||
>{loading.save ? $t('forms.saving') : $t('forms.save')}</button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler, generatePassword } from '$lib/database';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -34,6 +34,30 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
await db.configureBuildPack({ id, buildPack });
|
||||
|
||||
// Generate default secrets
|
||||
if (buildPack === 'laravel') {
|
||||
let found = await db.isSecretExists({ id, name: 'APP_ENV', isPRMRSecret: false });
|
||||
if (!found) {
|
||||
await db.createSecret({
|
||||
id,
|
||||
name: 'APP_ENV',
|
||||
value: 'production',
|
||||
isBuildSecret: false,
|
||||
isPRMRSecret: false
|
||||
});
|
||||
}
|
||||
found = await db.isSecretExists({ id, name: 'APP_KEY', isPRMRSecret: false });
|
||||
if (!found) {
|
||||
await db.createSecret({
|
||||
id,
|
||||
name: 'APP_KEY',
|
||||
value: generatePassword(32),
|
||||
isBuildSecret: false,
|
||||
isPRMRSecret: false
|
||||
});
|
||||
}
|
||||
}
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -31,11 +31,12 @@
|
||||
|
||||
import { buildPacks, findBuildPack, scanningTemplates } from '$lib/components/templates';
|
||||
import BuildPack from './_BuildPack.svelte';
|
||||
import { page, session } from '$app/stores';
|
||||
import { page } from '$app/stores';
|
||||
import { get } from '$lib/api';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { gitTokens } from '$lib/store';
|
||||
import { browser } from '$app/env';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
const { id } = $page.params;
|
||||
|
||||
@@ -84,13 +85,14 @@
|
||||
const composerPHP = files.find(
|
||||
(file) => file.name === 'composer.json' && file.type === 'blob'
|
||||
);
|
||||
const laravel = files.find((file) => file.name === 'artisan' && file.type === 'blob');
|
||||
|
||||
if (yarnLock) packageManager = 'yarn';
|
||||
if (pnpmLock) packageManager = 'pnpm';
|
||||
|
||||
if (dockerfile) {
|
||||
foundConfig = findBuildPack('docker', packageManager);
|
||||
} else if (packageJson) {
|
||||
} else if (packageJson && !laravel) {
|
||||
const path = packageJson.path;
|
||||
const data = await get(
|
||||
`${apiUrl}/v4/projects/${projectId}/repository/files/${path}/raw?ref=${branch}`,
|
||||
@@ -106,8 +108,10 @@
|
||||
foundConfig = findBuildPack('python');
|
||||
} else if (indexHtml) {
|
||||
foundConfig = findBuildPack('static', packageManager);
|
||||
} else if (indexPHP || composerPHP) {
|
||||
} else if ((indexPHP || composerPHP) && !laravel) {
|
||||
foundConfig = findBuildPack('php');
|
||||
} else if (laravel) {
|
||||
foundConfig = findBuildPack('laravel');
|
||||
} else {
|
||||
foundConfig = findBuildPack('node', packageManager);
|
||||
}
|
||||
@@ -133,13 +137,14 @@
|
||||
const composerPHP = files.find(
|
||||
(file) => file.name === 'composer.json' && file.type === 'file'
|
||||
);
|
||||
const laravel = files.find((file) => file.name === 'artisan' && file.type === 'file');
|
||||
|
||||
if (yarnLock) packageManager = 'yarn';
|
||||
if (pnpmLock) packageManager = 'pnpm';
|
||||
|
||||
if (dockerfile) {
|
||||
foundConfig = findBuildPack('docker', packageManager);
|
||||
} else if (packageJson) {
|
||||
} else if (packageJson && !laravel) {
|
||||
const data = await get(`${packageJson.git_url}`, {
|
||||
Authorization: `Bearer ${$gitTokens.githubToken}`,
|
||||
Accept: 'application/vnd.github.v2.raw'
|
||||
@@ -152,8 +157,10 @@
|
||||
foundConfig = findBuildPack('python');
|
||||
} else if (indexHtml) {
|
||||
foundConfig = findBuildPack('static', packageManager);
|
||||
} else if (indexPHP || composerPHP) {
|
||||
} else if ((indexPHP || composerPHP) && !laravel) {
|
||||
foundConfig = findBuildPack('php');
|
||||
} else if (laravel) {
|
||||
foundConfig = findBuildPack('laravel');
|
||||
} else {
|
||||
foundConfig = findBuildPack('node', packageManager);
|
||||
}
|
||||
@@ -204,24 +211,27 @@
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">Configure Build Pack</div>
|
||||
<div class="mr-4 text-2xl tracking-tight">
|
||||
{$t('application.configuration.configure_build_pack')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if scanning}
|
||||
<div class="flex justify-center space-x-1 p-6 font-bold">
|
||||
<div class="text-xl tracking-tight">Scanning repository to suggest a build pack for you...</div>
|
||||
<div class="text-xl tracking-tight">
|
||||
{$t('application.configuration.scanning_repository_suggest_build_pack')}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
{#if packageManager === 'yarn' || packageManager === 'pnpm'}
|
||||
<div class="flex justify-center p-6">
|
||||
Found lock file for <span class="font-bold text-orange-500 pl-1">{packageManager}</span>.
|
||||
Using it for predefined commands commands.
|
||||
{@html $t('application.configuration.found_lock_file', { packageManager })}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="max-w-7xl mx-auto flex flex-wrap justify-center">
|
||||
{#each buildPacks as buildPack}
|
||||
<div class="p-2">
|
||||
<BuildPack {buildPack} {scanning} {packageManager} bind:foundConfig />
|
||||
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { goto } from '$app/navigation';
|
||||
import { post } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
const { id } = $page.params;
|
||||
const from = $page.url.searchParams.get('from');
|
||||
@@ -60,12 +61,14 @@
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">Configure Destination</div>
|
||||
<div class="mr-4 text-2xl tracking-tight">
|
||||
{$t('application.configuration.configure_destination')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center">
|
||||
{#if !destinations || ownDestinations.length === 0}
|
||||
<div class="flex-col">
|
||||
<div class="pb-2">No configurable Destination found</div>
|
||||
<div class="pb-2">{$t('application.configuration.no_configurable_destination')}</div>
|
||||
<div class="flex justify-center">
|
||||
<a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500">
|
||||
<svg
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
export let application;
|
||||
export let appId;
|
||||
|
||||
@@ -26,7 +28,9 @@
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">Select a Repository / Project</div>
|
||||
<div class="mr-4 text-2xl tracking-tight">
|
||||
{$t('application.configuration.select_a_repository_project')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-center">
|
||||
{#if application.gitSource.type === 'github'}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { goto } from '$app/navigation';
|
||||
import { post } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
const { id } = $page.params;
|
||||
const from = $page.url.searchParams.get('from');
|
||||
@@ -72,12 +73,14 @@
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">Select a Git Source</div>
|
||||
<div class="mr-4 text-2xl tracking-tight">
|
||||
{$t('application.configuration.select_a_git_source')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center">
|
||||
{#if !filteredSources || ownSources.length === 0}
|
||||
<div class="flex-col">
|
||||
<div class="pb-2 text-center">No configurable Git Source found</div>
|
||||
<div class="pb-2 text-center">{$t('application.configuration.no_configurable_git')}</div>
|
||||
<div class="flex justify-center">
|
||||
<button on:click={newSource} class="add-icon bg-orange-600 hover:bg-orange-500">
|
||||
<svg
|
||||
@@ -139,7 +142,7 @@
|
||||
<div class="font-bold text-xl text-center truncate">{source.name}</div>
|
||||
{#if source.gitlabApp && !source.gitlabAppId}
|
||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||
Configuration missing
|
||||
{$t('application.configuration.configuration_missing')}
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
@@ -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
|
||||
@@ -35,6 +36,7 @@ export const post: RequestHandler = async (event) => {
|
||||
data: {
|
||||
id: buildId,
|
||||
applicationId: id,
|
||||
branch: applicationFound.branch,
|
||||
destinationDockerId: applicationFound.destinationDocker.id,
|
||||
gitSourceId: applicationFound.gitSource.id,
|
||||
githubAppId: applicationFound.gitSource.githubApp?.id,
|
||||
@@ -44,15 +46,23 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
});
|
||||
if (pullmergeRequestId) {
|
||||
await buildQueue.add(buildId, {
|
||||
await buildQueue.add(
|
||||
buildId,
|
||||
{
|
||||
build_id: buildId,
|
||||
type: 'manual',
|
||||
...applicationFound,
|
||||
sourceBranch: branch,
|
||||
pullmergeRequestId
|
||||
});
|
||||
},
|
||||
{ jobId: buildId }
|
||||
);
|
||||
} else {
|
||||
await buildQueue.add(buildId, { build_id: buildId, type: 'manual', ...applicationFound });
|
||||
await buildQueue.add(
|
||||
buildId,
|
||||
{ build_id: buildId, type: 'manual', ...applicationFound },
|
||||
{ jobId: buildId }
|
||||
);
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
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) => {
|
||||
@@ -15,17 +13,20 @@ export const get: RequestHandler = async (event) => {
|
||||
|
||||
const appId = process.env['COOLIFY_APP_ID'];
|
||||
let isRunning = false;
|
||||
let isExited = false;
|
||||
let githubToken = event.locals.cookies?.githubToken || null;
|
||||
let gitlabToken = event.locals.cookies?.gitlabToken || null;
|
||||
try {
|
||||
const application = await db.getApplication({ id, teamId });
|
||||
if (application.destinationDockerId) {
|
||||
isRunning = await checkContainer(application.destinationDocker.engine, id);
|
||||
isExited = await isContainerExited(application.destinationDocker.engine, id);
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
isRunning,
|
||||
isExited,
|
||||
application,
|
||||
appId,
|
||||
githubToken,
|
||||
@@ -49,6 +50,7 @@ export const post: RequestHandler = async (event) => {
|
||||
buildPack,
|
||||
fqdn,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@@ -56,9 +58,18 @@ export const post: RequestHandler = async (event) => {
|
||||
publishDirectory,
|
||||
pythonWSGI,
|
||||
pythonModule,
|
||||
pythonVariable
|
||||
pythonVariable,
|
||||
dockerFileLocation,
|
||||
denoMainFile,
|
||||
denoOptions,
|
||||
baseImage,
|
||||
baseBuildImage
|
||||
} = await event.request.json();
|
||||
if (port) port = Number(port);
|
||||
if (exposePort) {
|
||||
exposePort = Number(exposePort);
|
||||
}
|
||||
if (denoOptions) denoOptions = denoOptions.trim();
|
||||
|
||||
try {
|
||||
const defaultConfiguration = await setDefaultConfiguration({
|
||||
@@ -68,7 +79,9 @@ export const post: RequestHandler = async (event) => {
|
||||
startCommand,
|
||||
buildCommand,
|
||||
publishDirectory,
|
||||
baseDirectory
|
||||
baseDirectory,
|
||||
dockerFileLocation,
|
||||
denoMainFile
|
||||
});
|
||||
await db.configureApplication({
|
||||
id,
|
||||
@@ -76,6 +89,7 @@ export const post: RequestHandler = async (event) => {
|
||||
name,
|
||||
fqdn,
|
||||
port,
|
||||
exposePort,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
@@ -84,6 +98,11 @@ export const post: RequestHandler = async (event) => {
|
||||
pythonWSGI,
|
||||
pythonModule,
|
||||
pythonVariable,
|
||||
dockerFileLocation,
|
||||
denoMainFile,
|
||||
denoOptions,
|
||||
baseImage,
|
||||
baseBuildImage,
|
||||
...defaultConfiguration
|
||||
});
|
||||
return { status: 201 };
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user