mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-24 20:49:32 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04a5b1bd4f | ||
|
|
31b3f58b2c | ||
|
|
9c173d1de0 | ||
|
|
e11b6d74ed | ||
|
|
c7efe899fa | ||
|
|
adcd68c1ab |
21
README.md
21
README.md
@@ -1,4 +1,3 @@
|
||||
|
||||
# Coolify
|
||||
|
||||
An open-source, hassle-free, self-hostable Heroku & Netlify alternative.
|
||||
@@ -7,8 +6,7 @@ An open-source, hassle-free, self-hostable Heroku & Netlify alternative.
|
||||
|
||||
[Small video](https://cdn.coollabs.io/assets/coolify/video/coolify.webm)
|
||||
|
||||
|
||||
## Installation
|
||||
## Installation
|
||||
|
||||
Installation is automated with the following command:
|
||||
|
||||
@@ -16,49 +14,52 @@ Installation is automated with the following command:
|
||||
/bin/bash -c "$(curl -fsSL https://get.coollabs.io/coolify/install.sh)"
|
||||
```
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
You can deploy any of the following applications, databases and services easily.
|
||||
|
||||
(constantly growing lists)
|
||||
|
||||
### Applications
|
||||
|
||||
With Github integration
|
||||
|
||||
- Static sites
|
||||
- NodeJS
|
||||
- VueJS
|
||||
- NuxtJS
|
||||
- NextJS
|
||||
- React/Preact
|
||||
- NextJS
|
||||
- Gatsby
|
||||
- Svelte
|
||||
- PHP
|
||||
- Rust
|
||||
- Rust
|
||||
- or any custom dockerfile
|
||||
|
||||
### Databases
|
||||
|
||||
- MongoDB
|
||||
- MySQL
|
||||
- PostgreSQL
|
||||
- CouchDB
|
||||
- Redis
|
||||
|
||||
### Services
|
||||
|
||||
- [Plausible Analytics](https://plausible.io)
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai)
|
||||
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
|
||||
- Email: [andras@coollabs.io](mailto:andras@coollabs.io)
|
||||
- Discord: [Invitation](https://discord.com/invite/bvS3WhR)
|
||||
- Discord: [Invitation](https://discord.gg/xhBCC7eGKw)
|
||||
|
||||
## Roadmap
|
||||
|
||||
[See the Roadmap here](https://github.com/coollabsio/coolify/projects/1)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Please see the [LICENSE](/LICENSE) file in our repository for the full text.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coolify",
|
||||
"description": "An open-source, hassle-free, self-hostable Heroku & Netlify alternative.",
|
||||
"version": "1.0.12",
|
||||
"version": "1.0.16",
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
"dev:docker:start": "docker-compose -f docker-compose-dev.yml up -d",
|
||||
@@ -14,8 +14,8 @@
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-node": "^1.0.0-next.20",
|
||||
"@sveltejs/kit": "1.0.0-next.107",
|
||||
"@sveltejs/adapter-node": "^1.0.0-next.24",
|
||||
"@sveltejs/kit": "1.0.0-next.113",
|
||||
"@types/dockerode": "^3.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
||||
"@typescript-eslint/parser": "^4.23.0",
|
||||
@@ -31,7 +31,7 @@
|
||||
"prettier-plugin-svelte": "^2.3.0",
|
||||
"svelte": "^3.38.2",
|
||||
"svelte-preprocess": "^4.7.3",
|
||||
"tailwindcss": "canary",
|
||||
"tailwindcss": "2.2.0-canary.8",
|
||||
"tslib": "^2.2.0",
|
||||
"typescript": "^4.2.4",
|
||||
"vite": "^2.3.2"
|
||||
|
||||
450
pnpm-lock.yaml
generated
450
pnpm-lock.yaml
generated
@@ -2,11 +2,11 @@ lockfileVersion: 5.3
|
||||
|
||||
specifiers:
|
||||
'@iarna/toml': ^2.2.5
|
||||
'@sveltejs/adapter-node': ^1.0.0-next.20
|
||||
'@sveltejs/kit': 1.0.0-next.107
|
||||
'@sveltejs/adapter-node': ^1.0.0-next.24
|
||||
'@sveltejs/kit': 1.0.0-next.113
|
||||
'@types/dockerode': ^3.2.3
|
||||
'@typescript-eslint/eslint-plugin': ^4.22.1
|
||||
'@typescript-eslint/parser': ^4.22.1
|
||||
'@typescript-eslint/eslint-plugin': ^4.23.0
|
||||
'@typescript-eslint/parser': ^4.23.0
|
||||
'@zerodevx/svelte-toast': ^0.3.0
|
||||
autoprefixer: ^10.2.5
|
||||
commander: ^7.2.0
|
||||
@@ -23,21 +23,21 @@ specifiers:
|
||||
generate-password: ^1.6.0
|
||||
js-yaml: ^4.1.0
|
||||
jsonwebtoken: ^8.5.1
|
||||
mongoose: ^5.12.7
|
||||
postcss: ^8.2.14
|
||||
mongoose: ^5.12.9
|
||||
postcss: ^8.2.15
|
||||
postcss-load-config: ^3.0.1
|
||||
prettier: ~2.3.0
|
||||
prettier-plugin-svelte: ^2.2.0
|
||||
prettier-plugin-svelte: ^2.3.0
|
||||
shelljs: ^0.8.4
|
||||
svelte: ^3.38.2
|
||||
svelte-kit-cookie-session: ^0.4.3
|
||||
svelte-preprocess: ^4.7.3
|
||||
svelte-select: ^3.17.0
|
||||
tailwindcss: canary
|
||||
tailwindcss: 2.2.0-canary.8
|
||||
tslib: ^2.2.0
|
||||
typescript: ^4.2.4
|
||||
unique-names-generator: ^4.5.0
|
||||
vite: ^2.2.4
|
||||
vite: ^2.3.2
|
||||
|
||||
dependencies:
|
||||
'@iarna/toml': 2.2.5
|
||||
@@ -51,34 +51,34 @@ dependencies:
|
||||
generate-password: 1.6.0
|
||||
js-yaml: 4.1.0
|
||||
jsonwebtoken: 8.5.1
|
||||
mongoose: 5.12.7
|
||||
mongoose: 5.12.9
|
||||
shelljs: 0.8.4
|
||||
svelte-kit-cookie-session: 0.4.3
|
||||
svelte-select: 3.17.0
|
||||
unique-names-generator: 4.5.0
|
||||
|
||||
devDependencies:
|
||||
'@sveltejs/adapter-node': 1.0.0-next.20
|
||||
'@sveltejs/kit': 1.0.0-next.107_svelte@3.38.2
|
||||
'@sveltejs/adapter-node': 1.0.0-next.24
|
||||
'@sveltejs/kit': 1.0.0-next.113_svelte@3.38.2
|
||||
'@types/dockerode': 3.2.3
|
||||
'@typescript-eslint/eslint-plugin': 4.22.1_b96c9280c6ac79e4dfcda76a733858e3
|
||||
'@typescript-eslint/parser': 4.22.1_eslint@7.26.0+typescript@4.2.4
|
||||
autoprefixer: 10.2.5_postcss@8.2.14
|
||||
cssnano: 5.0.2_postcss@8.2.14
|
||||
'@typescript-eslint/eslint-plugin': 4.23.0_7cfaee78cda985ff9bc3d8b5cb92a136
|
||||
'@typescript-eslint/parser': 4.23.0_eslint@7.26.0+typescript@4.2.4
|
||||
autoprefixer: 10.2.5_postcss@8.2.15
|
||||
cssnano: 5.0.2_postcss@8.2.15
|
||||
dotenv-extended: 2.9.0
|
||||
eslint: 7.26.0
|
||||
eslint-config-prettier: 8.3.0_eslint@7.26.0
|
||||
eslint-plugin-svelte3: 3.2.0_eslint@7.26.0+svelte@3.38.2
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-load-config: 3.0.1
|
||||
prettier: 2.3.0
|
||||
prettier-plugin-svelte: 2.2.0_prettier@2.3.0+svelte@3.38.2
|
||||
prettier-plugin-svelte: 2.3.0_prettier@2.3.0+svelte@3.38.2
|
||||
svelte: 3.38.2
|
||||
svelte-preprocess: 4.7.3_bc60392b3f6116f56fa9e4496adda117
|
||||
tailwindcss: 2.2.0-canary.6_b46308bb95614802e1b6d1dce4e25ea6
|
||||
svelte-preprocess: 4.7.3_5944bffd99aeae72e0daf7602e6be64e
|
||||
tailwindcss: 2.2.0-canary.8_797cfc3ceddb49f111b184f6879a433f
|
||||
tslib: 2.2.0
|
||||
typescript: 4.2.4
|
||||
vite: 2.2.4
|
||||
vite: 2.3.2
|
||||
|
||||
packages:
|
||||
|
||||
@@ -158,28 +158,28 @@ packages:
|
||||
picomatch: 2.2.3
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-node/1.0.0-next.20:
|
||||
resolution: {integrity: sha512-ornNW289XN4TyY3tRXVGH38FxrqLsw1MHn4oq92T7U6EqnSbw/22waMZnPBQUxcj5GxZsXuuFjeBjn1BWt0x2A==}
|
||||
/@sveltejs/adapter-node/1.0.0-next.24:
|
||||
resolution: {integrity: sha512-33IbIYsIOq3LapSlW519oWuFvEHh8EHqU4FsOg1UZaF2EE5CkOh23SXjv0H9RT0iLCxvhifqiFN+vb/vlDRSWw==}
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.107_svelte@3.38.2:
|
||||
resolution: {integrity: sha512-HxomGMWbuWTU8lahk98hqIL/xjW6HzNTAvTZVMnnmlMHN1N8x7+abkz4Y09Zhu8lZkZpFuqBj2UlsgPW4uJXtw==}
|
||||
engines: {node: '>= 12.17.0'}
|
||||
/@sveltejs/kit/1.0.0-next.113_svelte@3.38.2:
|
||||
resolution: {integrity: sha512-X7feyAD9ZWQ79W5MXgF0t3JBsA+pTqCrnl2l8y8PNCP7pEJwizPCfmFAjwx7luz3OZUA2FTFBB7bqG7K1IUaYg==}
|
||||
engines: {node: ^12.20 || ^14.13.1 || >= 16}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
svelte: ^3.38.2
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte': 1.0.0-next.10_svelte@3.38.2+vite@2.3.2
|
||||
'@sveltejs/vite-plugin-svelte': 1.0.0-next.10_svelte@3.38.2+vite@2.3.6
|
||||
cheap-watch: 1.0.3
|
||||
sade: 1.7.4
|
||||
svelte: 3.38.2
|
||||
vite: 2.3.2
|
||||
vite: 2.3.6
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@sveltejs/vite-plugin-svelte/1.0.0-next.10_svelte@3.38.2+vite@2.3.2:
|
||||
/@sveltejs/vite-plugin-svelte/1.0.0-next.10_svelte@3.38.2+vite@2.3.6:
|
||||
resolution: {integrity: sha512-ImvxbhPePm2hWNTKBSA3LHAYGwiEjHjvvgfPLXm4R87sfZ+BMXql9jBmDpzUC/URBLT4BB3Jxos/i523qkJBHg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
@@ -195,7 +195,7 @@ packages:
|
||||
source-map: 0.7.3
|
||||
svelte: 3.38.2
|
||||
svelte-hmr: 0.14.3_svelte@3.38.2
|
||||
vite: 2.3.2
|
||||
vite: 2.3.6
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- supports-color
|
||||
@@ -246,8 +246,8 @@ packages:
|
||||
'@types/node': 15.0.1
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/4.22.1_b96c9280c6ac79e4dfcda76a733858e3:
|
||||
resolution: {integrity: sha512-kVTAghWDDhsvQ602tHBc6WmQkdaYbkcTwZu+7l24jtJiYvm9l+/y/b2BZANEezxPDiX5MK2ZecE+9BFi/YJryw==}
|
||||
/@typescript-eslint/eslint-plugin/4.23.0_7cfaee78cda985ff9bc3d8b5cb92a136:
|
||||
resolution: {integrity: sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^4.0.0
|
||||
@@ -257,9 +257,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/experimental-utils': 4.22.1_eslint@7.26.0+typescript@4.2.4
|
||||
'@typescript-eslint/parser': 4.22.1_eslint@7.26.0+typescript@4.2.4
|
||||
'@typescript-eslint/scope-manager': 4.22.1
|
||||
'@typescript-eslint/experimental-utils': 4.23.0_eslint@7.26.0+typescript@4.2.4
|
||||
'@typescript-eslint/parser': 4.23.0_eslint@7.26.0+typescript@4.2.4
|
||||
'@typescript-eslint/scope-manager': 4.23.0
|
||||
debug: 4.3.1
|
||||
eslint: 7.26.0
|
||||
functional-red-black-tree: 1.0.1
|
||||
@@ -272,16 +272,16 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/experimental-utils/4.22.1_eslint@7.26.0+typescript@4.2.4:
|
||||
resolution: {integrity: sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ==}
|
||||
/@typescript-eslint/experimental-utils/4.23.0_eslint@7.26.0+typescript@4.2.4:
|
||||
resolution: {integrity: sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
peerDependencies:
|
||||
eslint: '*'
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.7
|
||||
'@typescript-eslint/scope-manager': 4.22.1
|
||||
'@typescript-eslint/types': 4.22.1
|
||||
'@typescript-eslint/typescript-estree': 4.22.1_typescript@4.2.4
|
||||
'@typescript-eslint/scope-manager': 4.23.0
|
||||
'@typescript-eslint/types': 4.23.0
|
||||
'@typescript-eslint/typescript-estree': 4.23.0_typescript@4.2.4
|
||||
eslint: 7.26.0
|
||||
eslint-scope: 5.1.1
|
||||
eslint-utils: 2.1.0
|
||||
@@ -290,8 +290,8 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser/4.22.1_eslint@7.26.0+typescript@4.2.4:
|
||||
resolution: {integrity: sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw==}
|
||||
/@typescript-eslint/parser/4.23.0_eslint@7.26.0+typescript@4.2.4:
|
||||
resolution: {integrity: sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||
@@ -300,9 +300,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 4.22.1
|
||||
'@typescript-eslint/types': 4.22.1
|
||||
'@typescript-eslint/typescript-estree': 4.22.1_typescript@4.2.4
|
||||
'@typescript-eslint/scope-manager': 4.23.0
|
||||
'@typescript-eslint/types': 4.23.0
|
||||
'@typescript-eslint/typescript-estree': 4.23.0_typescript@4.2.4
|
||||
debug: 4.3.1
|
||||
eslint: 7.26.0
|
||||
typescript: 4.2.4
|
||||
@@ -310,21 +310,21 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager/4.22.1:
|
||||
resolution: {integrity: sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g==}
|
||||
/@typescript-eslint/scope-manager/4.23.0:
|
||||
resolution: {integrity: sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w==}
|
||||
engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 4.22.1
|
||||
'@typescript-eslint/visitor-keys': 4.22.1
|
||||
'@typescript-eslint/types': 4.23.0
|
||||
'@typescript-eslint/visitor-keys': 4.23.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/types/4.22.1:
|
||||
resolution: {integrity: sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw==}
|
||||
/@typescript-eslint/types/4.23.0:
|
||||
resolution: {integrity: sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw==}
|
||||
engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree/4.22.1_typescript@4.2.4:
|
||||
resolution: {integrity: sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A==}
|
||||
/@typescript-eslint/typescript-estree/4.23.0_typescript@4.2.4:
|
||||
resolution: {integrity: sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
@@ -332,8 +332,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 4.22.1
|
||||
'@typescript-eslint/visitor-keys': 4.22.1
|
||||
'@typescript-eslint/types': 4.23.0
|
||||
'@typescript-eslint/visitor-keys': 4.23.0
|
||||
debug: 4.3.1
|
||||
globby: 11.0.3
|
||||
is-glob: 4.0.1
|
||||
@@ -344,11 +344,11 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/visitor-keys/4.22.1:
|
||||
resolution: {integrity: sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ==}
|
||||
/@typescript-eslint/visitor-keys/4.23.0:
|
||||
resolution: {integrity: sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg==}
|
||||
engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 4.22.1
|
||||
'@typescript-eslint/types': 4.23.0
|
||||
eslint-visitor-keys: 2.0.0
|
||||
dev: true
|
||||
|
||||
@@ -463,18 +463,13 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/at-least-node/1.0.0:
|
||||
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
dev: true
|
||||
|
||||
/auto-parse/1.8.0:
|
||||
resolution: {integrity: sha512-Uri4uC+K5cSi5hjM4snFrqPrjqUpwxeSW5EMTPvN7Ju3PlDzmXXDr5tjdzxPvvwgT3J7bmMDJ3Rm625nbrc72A==}
|
||||
dependencies:
|
||||
typpy: 2.3.11
|
||||
dev: true
|
||||
|
||||
/autoprefixer/10.2.5_postcss@8.2.14:
|
||||
/autoprefixer/10.2.5_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
hasBin: true
|
||||
@@ -486,7 +481,7 @@ packages:
|
||||
colorette: 1.2.2
|
||||
fraction.js: 4.0.13
|
||||
normalize-range: 0.1.2
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
@@ -738,13 +733,13 @@ packages:
|
||||
resolution: {integrity: sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==}
|
||||
dev: true
|
||||
|
||||
/css-declaration-sorter/6.0.0_postcss@8.2.14:
|
||||
/css-declaration-sorter/6.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-S0TE4E0ha5+tBHdLWPc5n+S8E4dFBS5xScPvgHkLNZwWvX4ISoFGhGeerLC9uS1cKA/sC+K2wHq6qEbcagT/fg==}
|
||||
engines: {node: '>= 10'}
|
||||
peerDependencies:
|
||||
postcss: ^8.0.9
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
timsort: 0.3.0
|
||||
dev: true
|
||||
|
||||
@@ -781,63 +776,63 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/cssnano-preset-default/5.0.1_postcss@8.2.14:
|
||||
/cssnano-preset-default/5.0.1_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-cfmfThYODGqhpQKDq9H0MTAqkMvZ3dGbOUTBKw0xWZiIycMqHid22LsJXJl4r1qX4qzDeKxcSyQ/Xb5Mu3Z//Q==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
css-declaration-sorter: 6.0.0_postcss@8.2.14
|
||||
cssnano-utils: 2.0.0_postcss@8.2.14
|
||||
postcss: 8.2.14
|
||||
postcss-calc: 8.0.0_postcss@8.2.14
|
||||
postcss-colormin: 5.0.0_postcss@8.2.14
|
||||
postcss-convert-values: 5.0.0_postcss@8.2.14
|
||||
postcss-discard-comments: 5.0.0_postcss@8.2.14
|
||||
postcss-discard-duplicates: 5.0.0_postcss@8.2.14
|
||||
postcss-discard-empty: 5.0.0_postcss@8.2.14
|
||||
postcss-discard-overridden: 5.0.0_postcss@8.2.14
|
||||
postcss-merge-longhand: 5.0.1_postcss@8.2.14
|
||||
postcss-merge-rules: 5.0.0_postcss@8.2.14
|
||||
postcss-minify-font-values: 5.0.0_postcss@8.2.14
|
||||
postcss-minify-gradients: 5.0.0_postcss@8.2.14
|
||||
postcss-minify-params: 5.0.0_postcss@8.2.14
|
||||
postcss-minify-selectors: 5.0.0_postcss@8.2.14
|
||||
postcss-normalize-charset: 5.0.0_postcss@8.2.14
|
||||
postcss-normalize-display-values: 5.0.0_postcss@8.2.14
|
||||
postcss-normalize-positions: 5.0.0_postcss@8.2.14
|
||||
postcss-normalize-repeat-style: 5.0.0_postcss@8.2.14
|
||||
postcss-normalize-string: 5.0.0_postcss@8.2.14
|
||||
postcss-normalize-timing-functions: 5.0.0_postcss@8.2.14
|
||||
postcss-normalize-unicode: 5.0.0_postcss@8.2.14
|
||||
postcss-normalize-url: 5.0.0_postcss@8.2.14
|
||||
postcss-normalize-whitespace: 5.0.0_postcss@8.2.14
|
||||
postcss-ordered-values: 5.0.0_postcss@8.2.14
|
||||
postcss-reduce-initial: 5.0.0_postcss@8.2.14
|
||||
postcss-reduce-transforms: 5.0.0_postcss@8.2.14
|
||||
postcss-svgo: 5.0.0_postcss@8.2.14
|
||||
postcss-unique-selectors: 5.0.0_postcss@8.2.14
|
||||
css-declaration-sorter: 6.0.0_postcss@8.2.15
|
||||
cssnano-utils: 2.0.0_postcss@8.2.15
|
||||
postcss: 8.2.15
|
||||
postcss-calc: 8.0.0_postcss@8.2.15
|
||||
postcss-colormin: 5.0.0_postcss@8.2.15
|
||||
postcss-convert-values: 5.0.0_postcss@8.2.15
|
||||
postcss-discard-comments: 5.0.0_postcss@8.2.15
|
||||
postcss-discard-duplicates: 5.0.0_postcss@8.2.15
|
||||
postcss-discard-empty: 5.0.0_postcss@8.2.15
|
||||
postcss-discard-overridden: 5.0.0_postcss@8.2.15
|
||||
postcss-merge-longhand: 5.0.1_postcss@8.2.15
|
||||
postcss-merge-rules: 5.0.0_postcss@8.2.15
|
||||
postcss-minify-font-values: 5.0.0_postcss@8.2.15
|
||||
postcss-minify-gradients: 5.0.0_postcss@8.2.15
|
||||
postcss-minify-params: 5.0.0_postcss@8.2.15
|
||||
postcss-minify-selectors: 5.0.0_postcss@8.2.15
|
||||
postcss-normalize-charset: 5.0.0_postcss@8.2.15
|
||||
postcss-normalize-display-values: 5.0.0_postcss@8.2.15
|
||||
postcss-normalize-positions: 5.0.0_postcss@8.2.15
|
||||
postcss-normalize-repeat-style: 5.0.0_postcss@8.2.15
|
||||
postcss-normalize-string: 5.0.0_postcss@8.2.15
|
||||
postcss-normalize-timing-functions: 5.0.0_postcss@8.2.15
|
||||
postcss-normalize-unicode: 5.0.0_postcss@8.2.15
|
||||
postcss-normalize-url: 5.0.0_postcss@8.2.15
|
||||
postcss-normalize-whitespace: 5.0.0_postcss@8.2.15
|
||||
postcss-ordered-values: 5.0.0_postcss@8.2.15
|
||||
postcss-reduce-initial: 5.0.0_postcss@8.2.15
|
||||
postcss-reduce-transforms: 5.0.0_postcss@8.2.15
|
||||
postcss-svgo: 5.0.0_postcss@8.2.15
|
||||
postcss-unique-selectors: 5.0.0_postcss@8.2.15
|
||||
dev: true
|
||||
|
||||
/cssnano-utils/2.0.0_postcss@8.2.14:
|
||||
/cssnano-utils/2.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-xvxmTszdrvSyTACdPe8VU5J6p4sm3egpgw54dILvNqt5eBUv6TFjACLhSxtRuEsxYrgy8uDy269YjScO5aKbGA==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
dev: true
|
||||
|
||||
/cssnano/5.0.2_postcss@8.2.14:
|
||||
/cssnano/5.0.2_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-8JK3EnPsjQsULme9/e5M2hF564f/480hwsdcHvQ7ZtAIMfQ1O3SCfs+b8Mjf5KJxhYApyRshR2QSovEJi2K72Q==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
cosmiconfig: 7.0.0
|
||||
cssnano-preset-default: 5.0.1_postcss@8.2.14
|
||||
cssnano-preset-default: 5.0.1_postcss@8.2.15
|
||||
is-resolvable: 1.1.0
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
dev: true
|
||||
|
||||
/csso/4.2.0:
|
||||
@@ -1049,8 +1044,8 @@ packages:
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
|
||||
/esbuild/0.9.7:
|
||||
resolution: {integrity: sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==}
|
||||
/esbuild/0.12.6:
|
||||
resolution: {integrity: sha512-RDvVLvAjsq/kIZJoneMiUOH7EE7t2QaW7T3Q7EdQij14+bZbDq5sndb0tTanmHIFSqZVMBMMyqzVHkS3dJobeA==}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
@@ -1268,11 +1263,10 @@ packages:
|
||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||
dev: false
|
||||
|
||||
/fs-extra/9.1.0:
|
||||
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
|
||||
engines: {node: '>=10'}
|
||||
/fs-extra/10.0.0:
|
||||
resolution: {integrity: sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
at-least-node: 1.0.0
|
||||
graceful-fs: 4.2.6
|
||||
jsonfile: 6.1.0
|
||||
universalify: 2.0.0
|
||||
@@ -1801,23 +1795,23 @@ packages:
|
||||
saslprep: 1.0.3
|
||||
dev: false
|
||||
|
||||
/mongoose-legacy-pluralize/1.0.2_mongoose@5.12.7:
|
||||
/mongoose-legacy-pluralize/1.0.2_mongoose@5.12.9:
|
||||
resolution: {integrity: sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==}
|
||||
peerDependencies:
|
||||
mongoose: '*'
|
||||
dependencies:
|
||||
mongoose: 5.12.7
|
||||
mongoose: 5.12.9
|
||||
dev: false
|
||||
|
||||
/mongoose/5.12.7:
|
||||
resolution: {integrity: sha512-BniNwACn7uflK2h+M3juvyLH5nn9JDFgnB5KE2EwWFwSrRyhSpPnCtanRKJW3OtMCJyPccMIjtGZxHNW7JfnIw==}
|
||||
/mongoose/5.12.9:
|
||||
resolution: {integrity: sha512-ZSDjW15DmUbHQcZ2PqoXsJeYnpYipISi6QJH/XHR9dcSg3IRNCa86apcTptBux03/YBPiSZlKNYUNDx7iuMWoA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
dependencies:
|
||||
'@types/mongodb': 3.6.12
|
||||
bson: 1.1.6
|
||||
kareem: 2.3.2
|
||||
mongodb: 3.6.6
|
||||
mongoose-legacy-pluralize: 1.0.2_mongoose@5.12.7
|
||||
mongoose-legacy-pluralize: 1.0.2_mongoose@5.12.9
|
||||
mpath: 0.8.3
|
||||
mquery: 3.2.5
|
||||
ms: 2.1.2
|
||||
@@ -1866,12 +1860,6 @@ packages:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
dev: false
|
||||
|
||||
/nanoid/3.1.22:
|
||||
resolution: {integrity: sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/nanoid/3.1.23:
|
||||
resolution: {integrity: sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
@@ -1917,11 +1905,6 @@ packages:
|
||||
boolbase: 1.0.0
|
||||
dev: true
|
||||
|
||||
/object-assign/4.1.1:
|
||||
resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/object-hash/2.1.1:
|
||||
resolution: {integrity: sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -1998,17 +1981,17 @@ packages:
|
||||
engines: {node: '>=8.6'}
|
||||
dev: true
|
||||
|
||||
/postcss-calc/8.0.0_postcss@8.2.14:
|
||||
/postcss-calc/8.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.2
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-selector-parser: 6.0.5
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-colormin/5.0.0_postcss@8.2.14:
|
||||
/postcss-colormin/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-Yt84+5V6CgS/AhK7d7MA58vG8dSZ7+ytlRtWLaQhag3HXOncTfmYpuUOX4cDoXjvLfw1sHRCHMiBjYhc35CymQ==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
@@ -2016,63 +1999,54 @@ packages:
|
||||
dependencies:
|
||||
browserslist: 4.16.6
|
||||
color: 3.1.3
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-convert-values/5.0.0_postcss@8.2.14:
|
||||
/postcss-convert-values/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-V5kmYm4xoBAjNs+eHY/6XzXJkkGeg4kwNf2ocfqhLb1WBPEa4oaSmoi1fnVO7Dkblqvus9h+AenDvhCKUCK7uQ==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-discard-comments/5.0.0_postcss@8.2.14:
|
||||
/postcss-discard-comments/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-Umig6Gxs8m20RihiXY6QkePd6mp4FxkA1Dg+f/Kd6uw0gEMfKRjDeQOyFkLibexbJJGHpE3lrN/Q0R9SMrUMbQ==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
dev: true
|
||||
|
||||
/postcss-discard-duplicates/5.0.0_postcss@8.2.14:
|
||||
/postcss-discard-duplicates/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-vEJJ+Y3pFUnO1FyCBA6PSisGjHtnphL3V6GsNvkASq/VkP3OX5/No5RYXXLxHa2QegStNzg6HYrYdo71uR4caQ==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
dev: true
|
||||
|
||||
/postcss-discard-empty/5.0.0_postcss@8.2.14:
|
||||
/postcss-discard-empty/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-+wigy099Y1xZxG36WG5L1f2zeH1oicntkJEW4TDIqKKDO2g9XVB3OhoiHTu08rDEjLnbcab4rw0BAccwi2VjiQ==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
dev: true
|
||||
|
||||
/postcss-discard-overridden/5.0.0_postcss@8.2.14:
|
||||
/postcss-discard-overridden/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-hybnScTaZM2iEA6kzVQ6Spozy7kVdLw+lGw8hftLlBEzt93uzXoltkYp9u0tI8xbfhxDLTOOzHsHQCkYdmzRUg==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
dev: true
|
||||
|
||||
/postcss-functions/3.0.0:
|
||||
resolution: {integrity: sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=}
|
||||
dependencies:
|
||||
glob: 7.1.7
|
||||
object-assign: 4.1.1
|
||||
postcss: 6.0.23
|
||||
postcss-value-parser: 3.3.1
|
||||
postcss: 8.2.15
|
||||
dev: true
|
||||
|
||||
/postcss-js/3.0.3:
|
||||
@@ -2091,19 +2065,19 @@ packages:
|
||||
import-cwd: 3.0.0
|
||||
dev: true
|
||||
|
||||
/postcss-merge-longhand/5.0.1_postcss@8.2.14:
|
||||
/postcss-merge-longhand/5.0.1_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-H1RO8le5deFGumQzuhJjuL0bIXPRysa+w7xtk5KrHe38oiaSS9ksPXDo24+IOS3SETPhip0J5+1uCOW+ALs3Yw==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
css-color-names: 1.0.1
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
stylehacks: 5.0.0_postcss@8.2.14
|
||||
stylehacks: 5.0.0_postcss@8.2.15
|
||||
dev: true
|
||||
|
||||
/postcss-merge-rules/5.0.0_postcss@8.2.14:
|
||||
/postcss-merge-rules/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-TfsXbKjNYCGfUPEXGIGPySnMiJbdS+3gcVeV8gwmJP4RajyKZHW8E0FYDL1WmggTj3hi+m+WUCAvqRpX2ut4Kg==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
@@ -2111,35 +2085,35 @@ packages:
|
||||
dependencies:
|
||||
browserslist: 4.16.6
|
||||
caniuse-api: 3.0.0
|
||||
cssnano-utils: 2.0.0_postcss@8.2.14
|
||||
postcss: 8.2.14
|
||||
cssnano-utils: 2.0.0_postcss@8.2.15
|
||||
postcss: 8.2.15
|
||||
postcss-selector-parser: 6.0.5
|
||||
vendors: 1.0.4
|
||||
dev: true
|
||||
|
||||
/postcss-minify-font-values/5.0.0_postcss@8.2.14:
|
||||
/postcss-minify-font-values/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-zi2JhFaMOcIaNxhndX5uhsqSY1rexKDp23wV8EOmC9XERqzLbHsoRye3aYF716Zm+hkcR4loqKDt8LZlmihwAg==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-minify-gradients/5.0.0_postcss@8.2.14:
|
||||
/postcss-minify-gradients/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-/jPtNgs6JySMwgsE5dPOq8a2xEopWTW3RyqoB9fLqxgR+mDUNLSi7joKd+N1z7FXWgVkc4l/dEBMXHgNAaUbvg==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
cssnano-utils: 2.0.0_postcss@8.2.14
|
||||
cssnano-utils: 2.0.0_postcss@8.2.15
|
||||
is-color-stop: 1.1.0
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-minify-params/5.0.0_postcss@8.2.14:
|
||||
/postcss-minify-params/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-KvZYIxTPBVKjdd+XgObq9A+Sfv8lMkXTpbZTsjhr42XbfWIeLaTItMlygsDWfjArEc3muUfDaUFgNSeDiJ5jug==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
@@ -2147,107 +2121,107 @@ packages:
|
||||
dependencies:
|
||||
alphanum-sort: 1.0.2
|
||||
browserslist: 4.16.6
|
||||
cssnano-utils: 2.0.0_postcss@8.2.14
|
||||
postcss: 8.2.14
|
||||
cssnano-utils: 2.0.0_postcss@8.2.15
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
uniqs: 2.0.0
|
||||
dev: true
|
||||
|
||||
/postcss-minify-selectors/5.0.0_postcss@8.2.14:
|
||||
/postcss-minify-selectors/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-cEM0O0eWwFIvmo6nfB0lH0vO/XFwgqIvymODbfPXZ1gTA3i76FKnb7TGUrEpiTxaXH6tgYQ6DcTHwRiRS+YQLQ==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
alphanum-sort: 1.0.2
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-selector-parser: 3.1.2
|
||||
dev: true
|
||||
|
||||
/postcss-nested/5.0.5_postcss@8.2.14:
|
||||
/postcss-nested/5.0.5_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
postcss: ^8.1.13
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-selector-parser: 6.0.6
|
||||
dev: true
|
||||
|
||||
/postcss-normalize-charset/5.0.0_postcss@8.2.14:
|
||||
/postcss-normalize-charset/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-pqsCkgo9KmQP0ew6DqSA+uP9YN6EfsW20pQ3JU5JoQge09Z6Too4qU0TNDsTNWuEaP8SWsMp+19l15210MsDZQ==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
dev: true
|
||||
|
||||
/postcss-normalize-display-values/5.0.0_postcss@8.2.14:
|
||||
/postcss-normalize-display-values/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-t4f2d//gH1f7Ns0Jq3eNdnWuPT7TeLuISZ6RQx4j8gpl5XrhkdshdNcOnlrEK48YU6Tcb6jqK7dorME3N4oOGA==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
cssnano-utils: 2.0.0_postcss@8.2.14
|
||||
postcss: 8.2.14
|
||||
cssnano-utils: 2.0.0_postcss@8.2.15
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-normalize-positions/5.0.0_postcss@8.2.14:
|
||||
/postcss-normalize-positions/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-0o6/qU5ky74X/eWYj/tv4iiKCm3YqJnrhmVADpIMNXxzFZywsSQxl8F7cKs8jQEtF3VrJBgcDHTexZy1zgDoYg==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-normalize-repeat-style/5.0.0_postcss@8.2.14:
|
||||
/postcss-normalize-repeat-style/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-KRT14JbrXKcFMYuc4q7lh8lvv8u22wLyMrq+UpHKLtbx2H/LOjvWXYdoDxmNrrrJzomAWL+ViEXr48/IhSUJnQ==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
cssnano-utils: 2.0.0_postcss@8.2.14
|
||||
postcss: 8.2.14
|
||||
cssnano-utils: 2.0.0_postcss@8.2.15
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-normalize-string/5.0.0_postcss@8.2.14:
|
||||
/postcss-normalize-string/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-wSO4pf7GNcDZpmelREWYADF1+XZWrAcbFLQCOqoE92ZwYgaP/RLumkUTaamEzdT2YKRZAH8eLLKGWotU/7FNPw==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-normalize-timing-functions/5.0.0_postcss@8.2.14:
|
||||
/postcss-normalize-timing-functions/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-TwPaDX+wl9wO3MUm23lzGmOzGCGKnpk+rSDgzB2INpakD5dgWR3L6bJq1P1LQYzBAvz8fRIj2NWdnZdV4EV98Q==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
cssnano-utils: 2.0.0_postcss@8.2.14
|
||||
postcss: 8.2.14
|
||||
cssnano-utils: 2.0.0_postcss@8.2.15
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-normalize-unicode/5.0.0_postcss@8.2.14:
|
||||
/postcss-normalize-unicode/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-2CpVoz/67rXU5s9tsPZDxG1YGS9OFHwoY9gsLAzrURrCxTAb0H7Vp87/62LvVPgRWTa5ZmvgmqTp2rL8tlm72A==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
browserslist: 4.16.6
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-normalize-url/5.0.0_postcss@8.2.14:
|
||||
/postcss-normalize-url/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-ICDaGFBqLgA3dlrCIRuhblLl80D13YtgEV9NJPTYJtgR72vu61KgxAHv+z/lKMs1EbwfSQa3ALjOFLSmXiE34A==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
@@ -2255,32 +2229,32 @@ packages:
|
||||
dependencies:
|
||||
is-absolute-url: 3.0.3
|
||||
normalize-url: 4.5.0
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-normalize-whitespace/5.0.0_postcss@8.2.14:
|
||||
/postcss-normalize-whitespace/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-KRnxQvQAVkJfaeXSz7JlnD9nBN9sFZF9lrk9452Q2uRoqrRSkinqifF8Iex7wZGei2DZVG/qpmDFDmRvbNAOGA==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-ordered-values/5.0.0_postcss@8.2.14:
|
||||
/postcss-ordered-values/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-dPr+SRObiHueCIc4IUaG0aOGQmYkuNu50wQvdXTGKy+rzi2mjmPsbeDsheLk5WPb9Zyf2tp8E+I+h40cnivm6g==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
cssnano-utils: 2.0.0_postcss@8.2.14
|
||||
postcss: 8.2.14
|
||||
cssnano-utils: 2.0.0_postcss@8.2.15
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
/postcss-reduce-initial/5.0.0_postcss@8.2.14:
|
||||
/postcss-reduce-initial/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-wR6pXUaFbSMG1oCKx8pKVA+rnSXCHlca5jMrlmkmif+uig0HNUTV9oGN5kjKsM3mATQAldv2PF9Tbl2vqLFjnA==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
@@ -2288,17 +2262,17 @@ packages:
|
||||
dependencies:
|
||||
browserslist: 4.16.6
|
||||
caniuse-api: 3.0.0
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
dev: true
|
||||
|
||||
/postcss-reduce-transforms/5.0.0_postcss@8.2.14:
|
||||
/postcss-reduce-transforms/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-iHdGODW4YzM3WjVecBhPQt6fpJC4lGQZxJKjkBNHpp2b8dzmvj0ogKThqya+IRodQEFzjfXgYeESkf172FH5Lw==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
cssnano-utils: 2.0.0_postcss@8.2.14
|
||||
postcss: 8.2.14
|
||||
cssnano-utils: 2.0.0_postcss@8.2.15
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
dev: true
|
||||
|
||||
@@ -2327,25 +2301,25 @@ packages:
|
||||
util-deprecate: 1.0.2
|
||||
dev: true
|
||||
|
||||
/postcss-svgo/5.0.0_postcss@8.2.14:
|
||||
/postcss-svgo/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-M3/VS4sFI1Yp9g0bPL+xzzCNz5iLdRUztoFaugMit5a8sMfkVzzhwqbsOlD8IFFymCdJDmXmh31waYHWw1K4BA==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-value-parser: 4.1.0
|
||||
svgo: 2.3.0
|
||||
dev: true
|
||||
|
||||
/postcss-unique-selectors/5.0.0_postcss@8.2.14:
|
||||
/postcss-unique-selectors/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-o9l4pF8SRn7aCMTmzb/kNv/kjV7wPZpZ8Nlb1Gq8v/Qvw969K1wanz1RVA0ehHzWe9+wHXaC2DvZlak/gdMJ5w==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
alphanum-sort: 1.0.2
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-selector-parser: 6.0.5
|
||||
uniqs: 2.0.0
|
||||
dev: true
|
||||
@@ -2358,33 +2332,6 @@ packages:
|
||||
resolution: {integrity: sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==}
|
||||
dev: true
|
||||
|
||||
/postcss/6.0.23:
|
||||
resolution: {integrity: sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
dependencies:
|
||||
chalk: 2.4.2
|
||||
source-map: 0.6.1
|
||||
supports-color: 5.5.0
|
||||
dev: true
|
||||
|
||||
/postcss/8.2.13:
|
||||
resolution: {integrity: sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
dependencies:
|
||||
colorette: 1.2.2
|
||||
nanoid: 3.1.22
|
||||
source-map: 0.6.1
|
||||
dev: true
|
||||
|
||||
/postcss/8.2.14:
|
||||
resolution: {integrity: sha512-+jD0ZijcvyCqPQo/m/CW0UcARpdFylq04of+Q7RKX6f/Tu+dvpUI/9Sp81+i6/vJThnOBX09Quw0ZLOVwpzX3w==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
dependencies:
|
||||
colorette: 1.2.2
|
||||
nanoid: 3.1.22
|
||||
source-map: 0.6.1
|
||||
dev: true
|
||||
|
||||
/postcss/8.2.15:
|
||||
resolution: {integrity: sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@@ -2399,8 +2346,8 @@ packages:
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dev: true
|
||||
|
||||
/prettier-plugin-svelte/2.2.0_prettier@2.3.0+svelte@3.38.2:
|
||||
resolution: {integrity: sha512-Xdmqgr71tAuMqqzNCK52/v94g/Yv7V7lz+nmbO9NEA+9ol15VV3uUHOfydMNOo3SWvFaVlBcp947ebEaMWqVfQ==}
|
||||
/prettier-plugin-svelte/2.3.0_prettier@2.3.0+svelte@3.38.2:
|
||||
resolution: {integrity: sha512-HTzXvSq7lWFuLsSaxYOUkGkVNCl3RrSjDCOgQjkBX5FQGmWjL8o3IFACSGhjPMMfWKADpapAr0zdbBWkND9mqw==}
|
||||
peerDependencies:
|
||||
prettier: ^1.16.4 || ^2.0.0
|
||||
svelte: ^3.2.0
|
||||
@@ -2760,14 +2707,14 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/stylehacks/5.0.0_postcss@8.2.14:
|
||||
/stylehacks/5.0.0_postcss@8.2.15:
|
||||
resolution: {integrity: sha512-QOWm6XivDLb+fqffTZP8jrmPmPITVChl2KCY2R05nsCWwLi3VGhCdVc3IVGNwd1zzTt1jPd67zIKjpQfxzQZeA==}
|
||||
engines: {node: ^10 || ^12 || >=14.0}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.1
|
||||
dependencies:
|
||||
browserslist: 4.16.6
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-selector-parser: 6.0.5
|
||||
dev: true
|
||||
|
||||
@@ -2799,7 +2746,7 @@ packages:
|
||||
salteen: 1.0.0
|
||||
dev: false
|
||||
|
||||
/svelte-preprocess/4.7.3_bc60392b3f6116f56fa9e4496adda117:
|
||||
/svelte-preprocess/4.7.3_5944bffd99aeae72e0daf7602e6be64e:
|
||||
resolution: {integrity: sha512-Zx1/xLeGOIBlZMGPRCaXtlMe4ZA0faato5Dc3CosEqwu75MIEPuOstdkH6cy+RYTUYynoxzNaDxkPX4DbrPwRA==}
|
||||
engines: {node: '>= 9.11.2'}
|
||||
requiresBuild: true
|
||||
@@ -2843,7 +2790,7 @@ packages:
|
||||
'@types/pug': 2.0.4
|
||||
'@types/sass': 1.16.0
|
||||
detect-indent: 6.0.0
|
||||
postcss: 8.2.14
|
||||
postcss: 8.2.15
|
||||
postcss-load-config: 3.0.1
|
||||
strip-indent: 3.0.0
|
||||
svelte: 3.38.2
|
||||
@@ -2886,8 +2833,8 @@ packages:
|
||||
strip-ansi: 6.0.0
|
||||
dev: true
|
||||
|
||||
/tailwindcss/2.2.0-canary.6_b46308bb95614802e1b6d1dce4e25ea6:
|
||||
resolution: {integrity: sha512-gXpjyw8g4Z565tG6yDyzh2VH3mX0riWXeujg/L6Ph4z/t5ztnqRF2QGXVT5toZYyl7LP02YTrfVqxzDY5vuJDw==}
|
||||
/tailwindcss/2.2.0-canary.8_797cfc3ceddb49f111b184f6879a433f:
|
||||
resolution: {integrity: sha512-IVgtzCS3HTq4lsK793isUIhbylCoG/WrarYpK8cAlbjOt5TwLJnhfp2GKjfzK3201eM4YawTjuRI+wesEy8XGg==}
|
||||
engines: {node: '>=12.13.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -2895,7 +2842,7 @@ packages:
|
||||
postcss: ^8.0.9
|
||||
dependencies:
|
||||
'@fullhuman/postcss-purgecss': 3.1.3
|
||||
autoprefixer: 10.2.5_postcss@8.2.14
|
||||
autoprefixer: 10.2.5_postcss@8.2.15
|
||||
bytes: 3.1.0
|
||||
chalk: 4.1.1
|
||||
chokidar: 3.5.1
|
||||
@@ -2904,7 +2851,7 @@ packages:
|
||||
didyoumean: 1.2.1
|
||||
dlv: 1.1.3
|
||||
fast-glob: 3.2.5
|
||||
fs-extra: 9.1.0
|
||||
fs-extra: 10.0.0
|
||||
html-tags: 3.1.0
|
||||
lodash: 4.17.21
|
||||
lodash.topath: 4.5.2
|
||||
@@ -2913,10 +2860,9 @@ packages:
|
||||
normalize-path: 3.0.0
|
||||
object-hash: 2.1.1
|
||||
parse-glob: 3.0.4
|
||||
postcss: 8.2.14
|
||||
postcss-functions: 3.0.0
|
||||
postcss: 8.2.15
|
||||
postcss-js: 3.0.3
|
||||
postcss-nested: 5.0.5_postcss@8.2.14
|
||||
postcss-nested: 5.0.5_postcss@8.2.15
|
||||
postcss-selector-parser: 6.0.6
|
||||
postcss-value-parser: 4.1.0
|
||||
pretty-hrtime: 1.0.3
|
||||
@@ -3046,19 +2992,6 @@ packages:
|
||||
resolution: {integrity: sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==}
|
||||
dev: true
|
||||
|
||||
/vite/2.2.4:
|
||||
resolution: {integrity: sha512-vnIwSNci+phFMp6krhy+FbYzKL0R67Sdt9mVZ96S27AewrApSJjTqncJcalk8sf60BgcbW4+1C6DFIWkxquO9g==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
esbuild: 0.9.7
|
||||
postcss: 8.2.13
|
||||
resolve: 1.20.0
|
||||
rollup: 2.46.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
dev: true
|
||||
|
||||
/vite/2.3.2:
|
||||
resolution: {integrity: sha512-QhLdOompDrfkyryCNTts9HE+eJhvhN9ibKNJ5Q8DpQai+6nOsuIlaveZNg67e1O/2QaWqXeBo82eHnAs1De2bQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -3072,6 +3005,19 @@ packages:
|
||||
fsevents: 2.3.2
|
||||
dev: true
|
||||
|
||||
/vite/2.3.6:
|
||||
resolution: {integrity: sha512-fsEpNKDHgh3Sn66JH06ZnUBnIgUVUtw6ucDhlOj1CEqxIkymU25yv1/kWDPlIjyYHnalr0cN6V+zzUJ+fmWHYw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
esbuild: 0.12.6
|
||||
postcss: 8.2.15
|
||||
resolve: 1.20.0
|
||||
rollup: 2.46.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
dev: true
|
||||
|
||||
/which/2.0.2:
|
||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
@@ -1,87 +1,163 @@
|
||||
<script>
|
||||
import { application } from '$store';
|
||||
import { VITE_GITHUB_APP_NAME } from '$lib/consts';
|
||||
import { application, isPullRequestPermissionsGranted } from '$store';
|
||||
import { onMount } from 'svelte';
|
||||
import TooltipInfo from '$components/TooltipInfo.svelte';
|
||||
import { request } from '$lib/request';
|
||||
import { page, session } from '$app/stores';
|
||||
import { browser } from '$app/env';
|
||||
|
||||
let domainInput;
|
||||
let loading = {
|
||||
previewDeployment: false
|
||||
};
|
||||
let howToActivate = false;
|
||||
const buildpacks = {
|
||||
static: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80
|
||||
},
|
||||
build: true
|
||||
build: true,
|
||||
start: false
|
||||
},
|
||||
nodejs: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000
|
||||
},
|
||||
build: true
|
||||
build: true,
|
||||
start: true
|
||||
},
|
||||
nestjs: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000
|
||||
},
|
||||
build: true,
|
||||
start: true
|
||||
},
|
||||
vuejs: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80
|
||||
},
|
||||
build: true
|
||||
build: true,
|
||||
start: false
|
||||
},
|
||||
nuxtjs: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000
|
||||
},
|
||||
build: true
|
||||
build: true,
|
||||
start: true
|
||||
},
|
||||
react: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80
|
||||
},
|
||||
build: true
|
||||
build: true,
|
||||
start: false
|
||||
},
|
||||
nextjs: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000
|
||||
},
|
||||
build: true
|
||||
build: true,
|
||||
start: true
|
||||
},
|
||||
gatsby: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000
|
||||
},
|
||||
build: true
|
||||
build: true,
|
||||
start: false
|
||||
},
|
||||
svelte: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80
|
||||
},
|
||||
build: true
|
||||
build: true,
|
||||
start: false
|
||||
},
|
||||
php: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80
|
||||
},
|
||||
build: false
|
||||
build: false,
|
||||
start: false
|
||||
},
|
||||
rust: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000
|
||||
},
|
||||
build: false
|
||||
build: false,
|
||||
start: false
|
||||
},
|
||||
docker: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000
|
||||
},
|
||||
build: false
|
||||
build: false,
|
||||
start: false
|
||||
},
|
||||
python: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 4000
|
||||
},
|
||||
build: false,
|
||||
start: false,
|
||||
custom: true
|
||||
}
|
||||
};
|
||||
async function setPreviewDeployment() {
|
||||
if ($application.general.isPreviewDeploymentEnabled) {
|
||||
const result = window.confirm(
|
||||
"Are you sure? It will delete all PR deployments - it's NOT reversible!"
|
||||
);
|
||||
if (result) {
|
||||
loading.previewDeployment = true;
|
||||
$application.general.isPreviewDeploymentEnabled =
|
||||
!$application.general.isPreviewDeploymentEnabled;
|
||||
if ($page.path !== '/application/new') {
|
||||
const config = await request(`/api/v1/application/config/previewDeployment`, $session, {
|
||||
body: {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch,
|
||||
isPreviewDeploymentEnabled: $application.general.isPreviewDeploymentEnabled
|
||||
}
|
||||
});
|
||||
}
|
||||
loading.previewDeployment = false;
|
||||
}
|
||||
} else {
|
||||
loading.previewDeployment = true;
|
||||
$application.general.isPreviewDeploymentEnabled =
|
||||
!$application.general.isPreviewDeploymentEnabled;
|
||||
$application.general.pullRequest = 0;
|
||||
if ($page.path !== '/application/new') {
|
||||
const config = await request(`/api/v1/application/config/previewDeployment`, $session, {
|
||||
body: {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch,
|
||||
isPreviewDeploymentEnabled: $application.general.isPreviewDeploymentEnabled
|
||||
}
|
||||
});
|
||||
}
|
||||
loading.previewDeployment = false;
|
||||
}
|
||||
}
|
||||
function selectBuildPack(event) {
|
||||
if (event.target.innerText === 'React/Preact') {
|
||||
$application.build.pack = 'react';
|
||||
@@ -89,9 +165,38 @@
|
||||
$application.build.pack = event.target.innerText.replace(/\./g, '').toLowerCase();
|
||||
}
|
||||
}
|
||||
async function openGithub() {
|
||||
if (browser) {
|
||||
const config = await request(`https://api.github.com/apps/${VITE_GITHUB_APP_NAME}`, $session);
|
||||
|
||||
let url = `https://github.com/settings/apps/${VITE_GITHUB_APP_NAME}/permissions`;
|
||||
if (config.owner.type === 'Organization') {
|
||||
url = `https://github.com/organizations/${config.owner.login}/settings/apps/${VITE_GITHUB_APP_NAME}/permissions`;
|
||||
}
|
||||
|
||||
const left = screen.width / 2 - 1020 / 2;
|
||||
const top = screen.height / 2 - 618 / 2;
|
||||
const newWindow = open(
|
||||
url,
|
||||
'Permission Update',
|
||||
'resizable=1, scrollbars=1, fullscreen=1, height=1000, width=1220,top=' +
|
||||
top +
|
||||
', left=' +
|
||||
left +
|
||||
', toolbar=0, menubar=0, status=0'
|
||||
);
|
||||
const timer = setInterval(async () => {
|
||||
if (newWindow?.closed) {
|
||||
clearInterval(timer);
|
||||
location.reload();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
onMount(() => {
|
||||
if(!$application.publish.domain) domainInput.focus();
|
||||
if (!$application.publish.domain) domainInput.focus();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
@@ -178,6 +283,14 @@
|
||||
>
|
||||
Rust
|
||||
</div>
|
||||
<div
|
||||
class={$application.build.pack === 'nestjs'
|
||||
? 'buildpack bg-red-500'
|
||||
: 'buildpack hover:border-red-500'}
|
||||
on:click={selectBuildPack}
|
||||
>
|
||||
NestJS
|
||||
</div>
|
||||
<div
|
||||
class={$application.build.pack === 'docker'
|
||||
? 'buildpack bg-purple-500'
|
||||
@@ -186,10 +299,139 @@
|
||||
>
|
||||
Docker
|
||||
</div>
|
||||
<div
|
||||
class={$application.build.pack === 'python'
|
||||
? 'buildpack bg-green-500'
|
||||
: 'buildpack hover:border-green-500'}
|
||||
on:click={selectBuildPack}
|
||||
>
|
||||
Python
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl font-bold border-gradient w-52">General settings</div>
|
||||
<div class="grid grid-cols-1 max-w-2xl md:mx-auto mx-6 justify-center items-center pt-10">
|
||||
<div>
|
||||
<ul class="divide-y divide-warmGray-800">
|
||||
<li class="pb-6 flex items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<p class="text-base font-bold text-warmGray-100">Preview deployments</p>
|
||||
<p class="text-sm font-medium text-warmGray-400">
|
||||
PR's will be deployed so you could review them easily
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if $isPullRequestPermissionsGranted}
|
||||
<div
|
||||
class="relative"
|
||||
class:animate-wiggle={loading.previewDeployment}
|
||||
class:opacity-25={loading.previewDeployment}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
disabled={loading.previewDeployment}
|
||||
on:click={setPreviewDeployment}
|
||||
aria-pressed="false"
|
||||
class="relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200"
|
||||
class:bg-green-600={$application.general.isPreviewDeploymentEnabled}
|
||||
class:bg-warmGray-700={!$application.general.isPreviewDeploymentEnabled}
|
||||
class:cursor-not-allowed={loading.previewDeployment}
|
||||
>
|
||||
<span class="sr-only">Use setting</span>
|
||||
<span
|
||||
class="pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"
|
||||
class:translate-x-5={$application.general.isPreviewDeploymentEnabled}
|
||||
class:translate-x-0={!$application.general.isPreviewDeploymentEnabled}
|
||||
>
|
||||
<span
|
||||
class=" ease-in duration-200 absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"
|
||||
class:opacity-0={$application.general.isPreviewDeploymentEnabled}
|
||||
class:opacity-100={!$application.general.isPreviewDeploymentEnabled}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg class="bg-white h-3 w-3 text-red-600" fill="none" viewBox="0 0 12 12">
|
||||
<path
|
||||
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ease-out duration-100 absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"
|
||||
aria-hidden="true"
|
||||
class:opacity-100={$application.general.isPreviewDeploymentEnabled}
|
||||
class:opacity-0={!$application.general.isPreviewDeploymentEnabled}
|
||||
>
|
||||
<svg
|
||||
class="bg-white h-3 w-3 text-green-600"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 12 12"
|
||||
>
|
||||
<path
|
||||
d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
{#if loading.previewDeployment}
|
||||
<div class="absolute left-0 bottom-0 -mb-4 -ml-2 text-xs font-bold">{$application.general.isPreviewDeploymentEnabled ? 'Enabling...' : 'Disabling...' }</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="relative">
|
||||
{#if !howToActivate}
|
||||
<button
|
||||
class="button py-2 px-2 bg-warmGray-800 hover:bg-warmGray-700 text-white"
|
||||
on:click={() => (howToActivate = !howToActivate)}>How to active this?</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="button py-2 px-2 bg-green-600 hover:bg-green-500 text-white"
|
||||
on:click={openGithub}>Open Github</button
|
||||
>
|
||||
{/if}
|
||||
{#if howToActivate}
|
||||
<div class="absolute right-0 w-64 z-10">
|
||||
<div class="bg-warmGray-800 p-4 my-2 rounded text-white">
|
||||
<div
|
||||
class="absolute right-0 top-0 p-2 my-3 mx-1 text-xs font-bold cursor-pointer hover:bg-warmGray-700"
|
||||
on:click={() => (howToActivate = false)}
|
||||
>
|
||||
X
|
||||
</div>
|
||||
<p class="text-sm font-medium text-warmGray-400">
|
||||
You need to add <span class="text-white">two new permissions</span> to your GitHub
|
||||
App:
|
||||
</p>
|
||||
<br />
|
||||
<p class="text-sm font-medium text-warmGray-400">
|
||||
1. In <span class="text-white">Repository permissions</span>, add
|
||||
<span class="text-white">Read-only</span>
|
||||
access to <span class="text-white">Pull requests</span>.
|
||||
</p>
|
||||
<br />
|
||||
<p class="text-sm font-medium text-warmGray-400">
|
||||
2. In <span class="text-white">Subscribe to events</span> section,
|
||||
<span class="text-white"> check Pull request</span> field.
|
||||
</p>
|
||||
<br />
|
||||
<p class="text-sm font-medium text-warmGray-400">
|
||||
3. You <span class="text-white">receive an email</span> where you need to
|
||||
<span class="text-white">accept the new permissions</span>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-flow-col gap-2 items-center pb-6">
|
||||
<div class="grid grid-flow-row">
|
||||
<label for="Domain" class="">Domain</label>
|
||||
@@ -253,53 +495,92 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-2xl font-bold w-40"
|
||||
class:border-gradient={buildpacks[$application.build.pack].build}
|
||||
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
|
||||
>
|
||||
Commands
|
||||
</div>
|
||||
<div class="text-2xl font-bold w-40 border-gradient">Commands</div>
|
||||
<div class=" max-w-2xl md:mx-auto mx-6 justify-center items-center pt-10 pb-32">
|
||||
<div class="grid grid-flow-col gap-2 items-center">
|
||||
<div class="grid grid-flow-row">
|
||||
<label
|
||||
for="installCommand"
|
||||
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
|
||||
>Install Command <TooltipInfo
|
||||
label="Command to run for installing dependencies. eg: yarn install."
|
||||
/>
|
||||
</label>
|
||||
{#if $application.build.pack === 'python'}
|
||||
<label for="ModulePackageName"
|
||||
>Module/Package Name<TooltipInfo
|
||||
label="The module/package name to start (eg: the entry filename [main], without the py extension. See gunicorn.org for more details)"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<input
|
||||
class="mb-6"
|
||||
class:bg-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:text-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].build}
|
||||
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:cursor-not-allowed={!buildpacks[$application.build.pack].build}
|
||||
id="installCommand"
|
||||
bind:value={$application.build.command.installation}
|
||||
placeholder="eg: yarn install"
|
||||
/>
|
||||
<label
|
||||
for="buildCommand"
|
||||
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
|
||||
>Build Command <TooltipInfo
|
||||
label="Command to run for building your application. If empty, no build phase initiated in the deploy process."
|
||||
/></label
|
||||
>
|
||||
<input
|
||||
class="mb-6"
|
||||
class:bg-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:text-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].build}
|
||||
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:cursor-not-allowed={!buildpacks[$application.build.pack].build}
|
||||
id="buildCommand"
|
||||
bind:value={$application.build.command.build}
|
||||
placeholder="eg: yarn build"
|
||||
/>
|
||||
<input
|
||||
class="mb-6"
|
||||
id="ModulePackageName"
|
||||
bind:value={$application.build.command.python.module}
|
||||
placeholder="main"
|
||||
/>
|
||||
<label for="ApplicationInstance"
|
||||
>Application Instance<TooltipInfo
|
||||
label="The instance name (the main function name. See gunicorn.org for more details)"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<input
|
||||
class="mb-6"
|
||||
id="ApplicationInstance"
|
||||
bind:value={$application.build.command.python.instance}
|
||||
placeholder="app"
|
||||
/>
|
||||
{:else}
|
||||
<label
|
||||
for="installCommand"
|
||||
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
|
||||
>Install Command <TooltipInfo
|
||||
label="Command to run for installing dependencies. eg: yarn install"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<input
|
||||
class="mb-6"
|
||||
class:bg-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:text-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].build}
|
||||
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:cursor-not-allowed={!buildpacks[$application.build.pack].build}
|
||||
id="installCommand"
|
||||
bind:value={$application.build.command.installation}
|
||||
placeholder="eg: yarn install"
|
||||
/>
|
||||
<label
|
||||
for="buildCommand"
|
||||
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
|
||||
>Build Command <TooltipInfo
|
||||
label="Command to run for building your application. If empty, no build phase initiated in the deploy process."
|
||||
/></label
|
||||
>
|
||||
<input
|
||||
class="mb-6"
|
||||
class:bg-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:text-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].build}
|
||||
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].build}
|
||||
class:cursor-not-allowed={!buildpacks[$application.build.pack].build}
|
||||
id="buildCommand"
|
||||
bind:value={$application.build.command.build}
|
||||
placeholder="eg: yarn build"
|
||||
/>
|
||||
<label
|
||||
for="startCommand"
|
||||
class:text-warmGray-800={!buildpacks[$application.build.pack].start}
|
||||
>Start Command <TooltipInfo
|
||||
label="Command to start the application. eg: yarn start"
|
||||
/></label
|
||||
>
|
||||
<input
|
||||
class="mb-6"
|
||||
class:bg-warmGray-900={!buildpacks[$application.build.pack].start}
|
||||
class:text-warmGray-900={!buildpacks[$application.build.pack].start}
|
||||
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].start}
|
||||
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].start}
|
||||
class:cursor-not-allowed={!buildpacks[$application.build.pack].start}
|
||||
id="startcommand"
|
||||
bind:value={$application.build.command.start}
|
||||
placeholder="eg: yarn start"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -309,4 +590,5 @@
|
||||
.buildpack {
|
||||
@apply px-6 py-2 mx-2 my-2 bg-warmGray-800 w-48 ease-in-out transform hover:scale-105 text-center rounded border-2 border-transparent border-dashed cursor-pointer transition duration-100;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
110
src/components/Application/ActiveTab/PullRequests.svelte
Normal file
110
src/components/Application/ActiveTab/PullRequests.svelte
Normal file
@@ -0,0 +1,110 @@
|
||||
<script>
|
||||
import { browser } from '$app/env';
|
||||
import { goto } from '$app/navigation';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { session } from '$app/stores';
|
||||
import { request } from '$lib/request';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { application, prApplication } from '$store';
|
||||
let loadPRDeployments = null;
|
||||
onMount(async () => {
|
||||
await getPRDeployments();
|
||||
loadPRDeployments = setInterval(async () => {
|
||||
await getPRDeployments();
|
||||
}, 1000);
|
||||
});
|
||||
onDestroy(() => {
|
||||
clearInterval(loadPRDeployments);
|
||||
});
|
||||
async function getPRDeployments() {
|
||||
const { configuration } = await request(`/api/v1/application/config`, $session, {
|
||||
body: {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch
|
||||
}
|
||||
});
|
||||
$prApplication = configuration.filter((c) => c.general.pullRequest !== 0);
|
||||
}
|
||||
async function removePR(prConfiguration) {
|
||||
const result = window.confirm("Are you sure? It's NOT reversible!");
|
||||
if (result) {
|
||||
await request(`/api/v1/application/remove`, $session, {
|
||||
body: {
|
||||
organization: prConfiguration.repository.organization,
|
||||
name: prConfiguration.repository.name,
|
||||
branch: prConfiguration.repository.branch,
|
||||
domain: prConfiguration.publish.domain
|
||||
}
|
||||
});
|
||||
|
||||
browser && toast.push('PR deployment removed.');
|
||||
const { configuration } = await request(`/api/v1/application/config`, $session, {
|
||||
body: {
|
||||
name: prConfiguration.repository.name,
|
||||
organization: prConfiguration.repository.organization,
|
||||
branch: prConfiguration.repository.branch
|
||||
}
|
||||
});
|
||||
$prApplication = configuration.filter((c) => c.general.pullRequest !== 0);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="text-2xl font-bold border-gradient w-48">Pull Requests</div>
|
||||
<div class="text-center pt-4">
|
||||
{#if $prApplication.length > 0}
|
||||
<div class="py-4 ">
|
||||
{#each $prApplication as pr}
|
||||
<div class="flex space-x-4 justify-center items-center">
|
||||
<div class="text-left font-bold tracking-tight ">
|
||||
{pr.publish.domain}
|
||||
</div>
|
||||
<a
|
||||
target="_blank"
|
||||
class="icon mx-2 "
|
||||
href={'https://' + pr.publish.domain + pr.publish.path}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
||||
/>
|
||||
</svg></a
|
||||
>
|
||||
<!-- <div class="flex-1" /> -->
|
||||
<button
|
||||
class="icon hover:text-red-500 hover:bg-warmGray-800"
|
||||
on:click={() => removePR(pr)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="font-bold text-center">No PR deployments found</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,8 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { session } from '$app/stores';
|
||||
import { githubRepositories, application, githubInstallations } from '$store';
|
||||
import {
|
||||
githubRepositories,
|
||||
application,
|
||||
githubInstallations,
|
||||
prApplication,
|
||||
initConf,
|
||||
isPullRequestPermissionsGranted
|
||||
} from '$store';
|
||||
|
||||
import { fade } from 'svelte/transition';
|
||||
import Loading from '$components/Loading.svelte';
|
||||
@@ -18,6 +25,7 @@
|
||||
};
|
||||
let branches = [];
|
||||
let relogin = false;
|
||||
let permissions = {};
|
||||
function dashify(str: string, options?: any) {
|
||||
if (typeof str !== 'string') return str;
|
||||
return str
|
||||
@@ -110,8 +118,17 @@
|
||||
$session
|
||||
);
|
||||
loading.branches = false;
|
||||
await loadPermissions();
|
||||
}
|
||||
async function loadPermissions() {
|
||||
const config = await request(
|
||||
`https://api.github.com/apps/${import.meta.env.VITE_GITHUB_APP_NAME}`,
|
||||
$session
|
||||
);
|
||||
if (config.permissions['pull_requests'] && config.events.includes('pull_request')) {
|
||||
$isPullRequestPermissionsGranted = true;
|
||||
}
|
||||
}
|
||||
|
||||
async function modifyGithubAppConfig() {
|
||||
if (browser) {
|
||||
const left = screen.width / 2 - 1020 / 2;
|
||||
@@ -128,20 +145,23 @@
|
||||
', toolbar=0, menubar=0, status=0'
|
||||
);
|
||||
const timer = setInterval(async () => {
|
||||
if (newWindow.closed) {
|
||||
if (newWindow?.closed) {
|
||||
clearInterval(timer);
|
||||
loading.github = true;
|
||||
|
||||
if ($application.repository.name) {
|
||||
try {
|
||||
const config = await request(`/api/v1/application/config`, $session, {
|
||||
const { configuration } = await request(`/api/v1/application/config`, $session, {
|
||||
body: {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch
|
||||
}
|
||||
});
|
||||
$application = { ...config };
|
||||
|
||||
$prApplication = configuration.filter((c) => c.general.pullRequest !== 0);
|
||||
$application = configuration.find((c) => c.general.pullRequest === 0);
|
||||
$initConf = JSON.parse(JSON.stringify($application));
|
||||
} catch (error) {
|
||||
browser && goto('/dashboard/applications', { replaceState: true });
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
', toolbar=0, menubar=0, status=0'
|
||||
);
|
||||
const timer = setInterval(() => {
|
||||
if (newWindow.closed) {
|
||||
if (newWindow?.closed) {
|
||||
clearInterval(timer);
|
||||
location.reload()
|
||||
}
|
||||
|
||||
@@ -3,22 +3,28 @@
|
||||
import { onDestroy } from 'svelte';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import Tooltip from '$components/Tooltip.svelte';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { page, session } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { browser } from '$app/env';
|
||||
async function removeApplication() {
|
||||
await request(`/api/v1/application/remove`, $session, {
|
||||
body: {
|
||||
organization: $application.repository.organization,
|
||||
name: $application.repository.name,
|
||||
branch: $application.repository.branch
|
||||
}
|
||||
});
|
||||
const result = window.confirm(
|
||||
"Are you sure? It will delete all deployments, including PR's - it's NOT reversible!"
|
||||
);
|
||||
if (result) {
|
||||
await request(`/api/v1/application/remove`, $session, {
|
||||
body: {
|
||||
organization: $application.repository.organization,
|
||||
name: $application.repository.name,
|
||||
branch: $application.repository.branch,
|
||||
domain: $application.publish.domain
|
||||
}
|
||||
});
|
||||
|
||||
browser && toast.push('Application removed.');
|
||||
$application = JSON.parse(JSON.stringify(initialApplication));
|
||||
browser && goto(`/dashboard/applications`, { replaceState: true });
|
||||
browser && toast.push('Application removed.');
|
||||
$application = JSON.parse(JSON.stringify(initialApplication));
|
||||
browser && goto(`/dashboard/applications`, { replaceState: true });
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -50,6 +56,7 @@
|
||||
// toast.push(error.error || error || 'Ooops something went wrong.');
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<nav class="flex text-white justify-end items-center m-4 fixed right-0 top-0 space-x-4 z-50">
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
<script>
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import templates from '$lib/api/applications/templates';
|
||||
import { application, dashboard } from '$store';
|
||||
import templates from '$lib/api/applications/packs/templates';
|
||||
import { application, dashboard, initConf, prApplication } from '$store';
|
||||
import General from '$components/Application/ActiveTab/General.svelte';
|
||||
import Secrets from '$components/Application/ActiveTab/Secrets.svelte';
|
||||
import Loading from '$components/Loading.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page, session } from '$app/stores';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { browser } from '$app/env';
|
||||
import PullRequests from './ActiveTab/PullRequests.svelte';
|
||||
|
||||
let activeTab = {
|
||||
general: true,
|
||||
buildStep: false,
|
||||
secrets: false
|
||||
secrets: false,
|
||||
pullRequests: false
|
||||
};
|
||||
function activateTab(tab) {
|
||||
if (activeTab.hasOwnProperty(tab)) {
|
||||
activeTab = {
|
||||
general: false,
|
||||
buildStep: false,
|
||||
pullRequests: false,
|
||||
secrets: false
|
||||
};
|
||||
activeTab[tab] = true;
|
||||
@@ -48,16 +49,7 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($page.path !== '/application/new') {
|
||||
const config = await request(`/api/v1/application/config`, $session, {
|
||||
body: {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch
|
||||
}
|
||||
});
|
||||
$application = { ...config };
|
||||
} else {
|
||||
if ($page.path === '/application/new') {
|
||||
try {
|
||||
const dir = await request(
|
||||
`https://api.github.com/repos/${$application.repository.organization}/${$application.repository.name}/contents/?ref=${$application.repository.branch}`,
|
||||
@@ -66,7 +58,7 @@
|
||||
const packageJson = dir.find((f) => f.type === 'file' && f.name === 'package.json');
|
||||
const Dockerfile = dir.find((f) => f.type === 'file' && f.name === 'Dockerfile');
|
||||
const CargoToml = dir.find((f) => f.type === 'file' && f.name === 'Cargo.toml');
|
||||
|
||||
const requirementsTXT = dir.find((f) => f.type === 'file' && f.name === 'requirements.txt');
|
||||
if (packageJson) {
|
||||
const { content } = await request(packageJson.git_url, $session);
|
||||
const packageJsonContent = JSON.parse(atob(content));
|
||||
@@ -80,10 +72,18 @@
|
||||
if (checkPackageJSONContents(dep)) {
|
||||
const config = templates[dep];
|
||||
$application.build.pack = config.pack;
|
||||
if (config.installation)
|
||||
if (config.installation) {
|
||||
$application.build.command.installation = config.installation;
|
||||
if (config.port) $application.publish.port = config.port;
|
||||
if (config.directory) $application.publish.directory = config.directory;
|
||||
}
|
||||
if (config.start) {
|
||||
$application.build.command.start = config.start;
|
||||
}
|
||||
if (config.port) {
|
||||
$application.publish.port = config.port;
|
||||
}
|
||||
if (config.directory) {
|
||||
$application.publish.directory = config.directory;
|
||||
}
|
||||
|
||||
if (packageJsonContent.scripts.hasOwnProperty('build') && config.build) {
|
||||
$application.build.command.build = config.build;
|
||||
@@ -94,6 +94,9 @@
|
||||
} else if (CargoToml) {
|
||||
$application.build.pack = 'rust';
|
||||
browser && toast.push(`Rust language detected. Default values set.`);
|
||||
} else if (requirementsTXT) {
|
||||
$application.build.pack = 'python';
|
||||
browser && toast.push('Python language detected. Default values set.');
|
||||
} else if (Dockerfile) {
|
||||
$application.build.pack = 'docker';
|
||||
browser && toast.push('Custom Dockerfile found. Build pack set to docker.');
|
||||
@@ -103,6 +106,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#await load()}
|
||||
@@ -124,6 +128,15 @@
|
||||
>
|
||||
Secrets
|
||||
</div>
|
||||
{#if $application.general.isPreviewDeploymentEnabled}
|
||||
<div
|
||||
on:click={() => activateTab('pullRequests')}
|
||||
class:text-green-500={activeTab.pullRequests}
|
||||
class="px-3 py-2 cursor-pointer hover:bg-warmGray-700 rounded-lg transition duration-100"
|
||||
>
|
||||
Pull Requests
|
||||
</div>
|
||||
{/if}
|
||||
</nav>
|
||||
</div>
|
||||
<div class="max-w-4xl mx-auto">
|
||||
@@ -132,6 +145,8 @@
|
||||
<General />
|
||||
{:else if activeTab.secrets}
|
||||
<Secrets />
|
||||
{:else if activeTab.pullRequests && $page.path !== '/application/new'}
|
||||
<PullRequests />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,16 +5,19 @@
|
||||
import Postgresql from './SVGs/Postgresql.svelte';
|
||||
import Mysql from './SVGs/Mysql.svelte';
|
||||
import CouchDb from './SVGs/CouchDb.svelte';
|
||||
import Redis from './SVGs/Redis.svelte';
|
||||
import { page, session } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { browser } from '$app/env';
|
||||
import Loading from '$components/Loading.svelte';
|
||||
|
||||
let type;
|
||||
let defaultDatabaseName;
|
||||
|
||||
let loading = false;
|
||||
async function deploy() {
|
||||
try {
|
||||
loading = true;
|
||||
await request(`/api/v1/databases/deploy`, $session, {
|
||||
body: {
|
||||
type,
|
||||
@@ -28,82 +31,106 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="text-center space-y-2 max-w-4xl mx-auto px-6" in:fade={{ duration: 100 }}>
|
||||
{#if $page.path === '/database/new'}
|
||||
<div class="flex justify-center space-x-4 font-bold pb-6">
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-green-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-green-600={type === 'mongodb'}
|
||||
on:click={() => (type = 'mongodb')}
|
||||
>
|
||||
<div class="flex items-center justify-center my-2">
|
||||
<MongoDb customClass="w-6" />
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<div class="text-center space-y-2 max-w-4xl mx-auto px-6" in:fade={{ duration: 100 }}>
|
||||
{#if $page.path === '/database/new'}
|
||||
<div class="flex justify-center space-x-4 font-bold pb-6">
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-green-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-green-600={type === 'mongodb'}
|
||||
on:click={() => (type = 'mongodb')}
|
||||
>
|
||||
<div class="flex items-center justify-center my-2">
|
||||
<MongoDb customClass="w-6" />
|
||||
</div>
|
||||
<div class="text-white">MongoDB</div>
|
||||
</div>
|
||||
<div class="text-white">MongoDB</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-red-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-red-600={type === 'couchdb'}
|
||||
on:click={() => (type = 'couchdb')}
|
||||
>
|
||||
<div class="flex items-center justify-center my-2">
|
||||
<CouchDb customClass="w-12 text-red-600 fill-current" />
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-red-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-red-600={type === 'couchdb'}
|
||||
on:click={() => (type = 'couchdb')}
|
||||
>
|
||||
<div class="flex items-center justify-center my-2">
|
||||
<CouchDb customClass="w-12 text-red-600 fill-current" />
|
||||
</div>
|
||||
<div class="text-white">Couchdb</div>
|
||||
</div>
|
||||
<div class="text-white">Couchdb</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-blue-600={type === 'postgresql'}
|
||||
on:click={() => (type = 'postgresql')}
|
||||
>
|
||||
<div class="flex items-center justify-center my-2">
|
||||
<Postgresql customClass="w-12" />
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-blue-600={type === 'postgresql'}
|
||||
on:click={() => (type = 'postgresql')}
|
||||
>
|
||||
<div class="flex items-center justify-center my-2">
|
||||
<Postgresql customClass="w-12" />
|
||||
</div>
|
||||
<div class="text-white">PostgreSQL</div>
|
||||
</div>
|
||||
<div class="text-white">PostgreSQL</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-orange-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-orange-600={type === 'mysql'}
|
||||
on:click={() => (type = 'mysql')}
|
||||
>
|
||||
<div class="flex items-center justify-center">
|
||||
<Mysql customClass="w-10" />
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-orange-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-orange-600={type === 'mysql'}
|
||||
on:click={() => (type = 'mysql')}
|
||||
>
|
||||
<div class="flex items-center justify-center">
|
||||
<Mysql customClass="w-10" />
|
||||
</div>
|
||||
<div class="text-white">MySQL</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-red-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-red-600={type === 'redis'}
|
||||
on:click={() => (type = 'redis')}
|
||||
>
|
||||
<div class="flex items-center justify-center">
|
||||
<Redis customClass="w-12" />
|
||||
</div>
|
||||
<div class="text-white">Redis</div>
|
||||
</div>
|
||||
<div class="text-white">MySQL</div>
|
||||
</div>
|
||||
|
||||
<!-- <button
|
||||
<!-- <button
|
||||
class="button bg-gray-500 p-2 text-white hover:bg-yellow-500 cursor-pointer w-32"
|
||||
on:click="{() => (type = 'clickhouse')}"
|
||||
class:bg-yellow-500="{type === 'clickhouse'}"
|
||||
>
|
||||
Clickhouse
|
||||
</button> -->
|
||||
</div>
|
||||
{#if type}
|
||||
<div class="flex justify-center space-x-4 items-center">
|
||||
<label for="defaultDB">Default database</label>
|
||||
<input id="defaultDB" class="w-64" placeholder="random" bind:value={defaultDatabaseName} />
|
||||
|
||||
<button
|
||||
class:bg-green-600={type === 'mongodb'}
|
||||
class:hover:bg-green-500={type === 'mongodb'}
|
||||
class:bg-blue-600={type === 'postgresql'}
|
||||
class:hover:bg-blue-500={type === 'postgresql'}
|
||||
class:bg-orange-600={type === 'mysql'}
|
||||
class:hover:bg-orange-500={type === 'mysql'}
|
||||
class:bg-red-600={type === 'couchdb'}
|
||||
class:hover:bg-red-500={type === 'couchdb'}
|
||||
class:bg-yellow-500={type === 'clickhouse'}
|
||||
class:hover:bg-yellow-400={type === 'clickhouse'}
|
||||
class="button p-2 w-32 text-white"
|
||||
on:click={deploy}>Deploy</button
|
||||
>
|
||||
</div>
|
||||
{#if type}
|
||||
<div class="flex justify-center space-x-4 items-center">
|
||||
{#if type !== 'redis'}
|
||||
<label for="defaultDB">Default database</label>
|
||||
<input
|
||||
id="defaultDB"
|
||||
class="w-64"
|
||||
placeholder="random"
|
||||
bind:value={defaultDatabaseName}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class:bg-green-600={type === 'mongodb'}
|
||||
class:hover:bg-green-500={type === 'mongodb'}
|
||||
class:bg-blue-600={type === 'postgresql'}
|
||||
class:hover:bg-blue-500={type === 'postgresql'}
|
||||
class:bg-orange-600={type === 'mysql'}
|
||||
class:hover:bg-orange-500={type === 'mysql'}
|
||||
class:bg-red-600={type === 'couchdb' || type === 'redis'}
|
||||
class:hover:bg-red-500={type === 'couchdb' || type === 'redis'}
|
||||
class:bg-yellow-500={type === 'clickhouse'}
|
||||
class:hover:bg-yellow-400={type === 'clickhouse'}
|
||||
class="button p-2 w-32 text-white"
|
||||
on:click={deploy}>Deploy</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
37
src/components/Database/SVGs/Redis.svelte
Normal file
37
src/components/Database/SVGs/Redis.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script>
|
||||
export let customClass;
|
||||
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={customClass}
|
||||
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
|
||||
><path
|
||||
id="a"
|
||||
d="m45.536 38.764c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.813s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z"
|
||||
/><path
|
||||
id="b"
|
||||
d="m45.536 28.733c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.935c2.332-.837 3.14-.867 5.126-.14s12.35 4.853 14.312 5.57 2.037 1.31.024 2.36z"
|
||||
/></defs
|
||||
><g transform="matrix(.848327 0 0 .848327 -7.883573 -9.449691)"
|
||||
><use fill="#a41e11" xlink:href="#a" /><path
|
||||
d="m45.536 34.95c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.936c2.332-.836 3.14-.867 5.126-.14s12.35 4.852 14.31 5.582 2.037 1.31.024 2.36z"
|
||||
fill="#d82c20"
|
||||
/><use fill="#a41e11" xlink:href="#a" y="-6.218" /><use fill="#d82c20" xlink:href="#b" /><path
|
||||
d="m45.536 26.098c-2.013 1.05-12.44 5.337-14.66 6.495s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.815s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z"
|
||||
fill="#a41e11"
|
||||
/><use fill="#d82c20" xlink:href="#b" y="-6.449" /><g fill="#fff"
|
||||
><path
|
||||
d="m29.096 20.712-1.182-1.965-3.774-.34 2.816-1.016-.845-1.56 2.636 1.03 2.486-.814-.672 1.612 2.534.95-3.268.34zm-6.296 3.912 8.74-1.342-2.64 3.872z"
|
||||
/><ellipse cx="20.444" cy="21.402" rx="4.672" ry="1.811" /></g
|
||||
><path d="m42.132 21.138-5.17 2.042-.004-4.087z" fill="#7a0c00" /><path
|
||||
d="m36.963 23.18-.56.22-5.166-2.042 5.723-2.264z"
|
||||
fill="#ad2115"
|
||||
/></g
|
||||
></svg
|
||||
>
|
||||
@@ -3,7 +3,7 @@
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import Loading from '../Loading.svelte';
|
||||
import Tooltip from '$components/Tooltip.svelte';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { page, session } from '$app/stores';
|
||||
import PasswordField from '$components/PasswordField.svelte';
|
||||
import { browser } from '$app/env';
|
||||
|
||||
8
src/global.d.ts
vendored
8
src/global.d.ts
vendored
@@ -36,6 +36,8 @@ export type Application = {
|
||||
deployId: string;
|
||||
nickname: string;
|
||||
workdir: string;
|
||||
isPreviewDeploymentEnabled: boolean;
|
||||
pullRequest: number;
|
||||
};
|
||||
build: {
|
||||
pack: string;
|
||||
@@ -43,6 +45,12 @@ export type Application = {
|
||||
command: {
|
||||
build: string | null;
|
||||
installation: string;
|
||||
start: string;
|
||||
python: {
|
||||
module?: string;
|
||||
instance?: string;
|
||||
}
|
||||
|
||||
};
|
||||
container: {
|
||||
name: string;
|
||||
|
||||
@@ -4,6 +4,9 @@ import { publicPages } from '$lib/consts';
|
||||
import mongoose from 'mongoose';
|
||||
import { verifyUserId } from '$lib/api/common';
|
||||
import { initializeSession } from 'svelte-kit-cookie-session';
|
||||
import { cleanupStuckedDeploymentsInDB } from '$lib/api/applications/cleanup';
|
||||
import { docker } from '$lib/api/docker';
|
||||
import Configuration from '$models/Configuration';
|
||||
|
||||
process.on('SIGINT', function () {
|
||||
mongoose.connection.close(function () {
|
||||
@@ -13,6 +16,7 @@ process.on('SIGINT', function () {
|
||||
});
|
||||
|
||||
async function connectMongoDB() {
|
||||
// TODO: Save configurations on start?
|
||||
const { MONGODB_USER, MONGODB_PASSWORD, MONGODB_HOST, MONGODB_PORT, MONGODB_DB } = process.env;
|
||||
try {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
@@ -27,19 +31,84 @@ async function connectMongoDB() {
|
||||
);
|
||||
}
|
||||
console.log('Connected to mongodb.');
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (mongoose.connection.readyState !== 1) connectMongoDB();
|
||||
(async () => {
|
||||
if (mongoose.connection.readyState !== 1) await connectMongoDB();
|
||||
try {
|
||||
await mongoose.connection.db.dropCollection('logs-servers');
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
try {
|
||||
await cleanupStuckedDeploymentsInDB();
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
try {
|
||||
const dockerServices = await docker.engine.listServices();
|
||||
let applications: any = dockerServices.filter(
|
||||
(r) =>
|
||||
r.Spec.Labels.managedBy === 'coolify' &&
|
||||
r.Spec.Labels.type === 'application' &&
|
||||
r.Spec.Labels.configuration
|
||||
);
|
||||
applications = applications.map((r) => {
|
||||
if (JSON.parse(r.Spec.Labels.configuration)) {
|
||||
return {
|
||||
configuration: JSON.parse(r.Spec.Labels.configuration),
|
||||
UpdatedAt: r.UpdatedAt
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
applications = [
|
||||
...new Map(
|
||||
applications.map((item) => [
|
||||
item.configuration.publish.domain + item.configuration.publish.path,
|
||||
item
|
||||
])
|
||||
).values()
|
||||
];
|
||||
for (const application of applications) {
|
||||
await Configuration.findOneAndUpdate({
|
||||
'repository.name': application.configuration.repository.name,
|
||||
'repository.organization': application.configuration.repository.organization,
|
||||
'repository.branch': application.configuration.repository.branch,
|
||||
'publish.domain': application.configuration.publish.domain
|
||||
}, {
|
||||
...application.configuration
|
||||
}, { upsert: true, new: true })
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
})()
|
||||
|
||||
export async function handle({ request, render }) {
|
||||
|
||||
export async function handle({ request, resolve }) {
|
||||
const { SECRETS_ENCRYPTION_KEY } = process.env;
|
||||
const session = initializeSession(request.headers, {
|
||||
secret: SECRETS_ENCRYPTION_KEY,
|
||||
cookie: { path: '/' }
|
||||
});
|
||||
let session;
|
||||
try {
|
||||
session = initializeSession(request.headers, {
|
||||
secret: SECRETS_ENCRYPTION_KEY,
|
||||
cookie: { path: '/' }
|
||||
});
|
||||
} catch(error) {
|
||||
return {
|
||||
status: 302,
|
||||
headers: {
|
||||
'set-cookie': 'kit.session=deleted;path=/;expires=Wed, 21 Oct 2015 07:28:00 GMT',
|
||||
location: '/'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
request.locals.session = session;
|
||||
if (session?.data?.coolToken) {
|
||||
try {
|
||||
@@ -49,7 +118,7 @@ export async function handle({ request, render }) {
|
||||
request.locals.session.destroy = true;
|
||||
}
|
||||
}
|
||||
const response = await render(request);
|
||||
const response = await resolve(request);
|
||||
if (!session['set-cookie']) {
|
||||
if (!session?.data?.coolToken && !publicPages.includes(request.path)) {
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Deployment from '$models/Logs/Deployment';
|
||||
import Deployment from '$models/Deployment';
|
||||
import { saveAppLog } from './logging';
|
||||
import * as packs from './packs';
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { docker } from '$lib/api/docker';
|
||||
import Deployment from '$models/Deployment';
|
||||
import { execShellAsync } from '../common';
|
||||
|
||||
export async function deleteSameDeployments(configuration) {
|
||||
@@ -10,36 +11,57 @@ export async function deleteSameDeployments(configuration) {
|
||||
const running = JSON.parse(s.Spec.Labels.configuration);
|
||||
if (
|
||||
running.repository.id === configuration.repository.id &&
|
||||
running.repository.branch === configuration.repository.branch
|
||||
running.repository.branch === configuration.repository.branch &&
|
||||
running.publish.domain === configuration.publish.domain
|
||||
) {
|
||||
await execShellAsync(`docker stack rm ${s.Spec.Labels['com.docker.stack.namespace']}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function cleanupStuckedDeploymentsInDB() {
|
||||
// Cleanup stucked deployments.
|
||||
await Deployment.updateMany(
|
||||
{ progress: { $in: ['queued', 'inprogress'] } },
|
||||
{ progress: 'failed' }
|
||||
);
|
||||
}
|
||||
export async function purgeImagesContainers(configuration, deleteAll = false) {
|
||||
const { name, tag } = configuration.build.container;
|
||||
await execShellAsync('docker container prune -f');
|
||||
if (deleteAll) {
|
||||
const IDsToDelete = (
|
||||
await execShellAsync(`docker images ls --filter=reference='${name}' --format '{{json .ID }}'`)
|
||||
)
|
||||
.trim()
|
||||
.replace(/"/g, '')
|
||||
.split('\n');
|
||||
if (IDsToDelete.length > 0)
|
||||
await execShellAsync(`docker rmi -f ${IDsToDelete.toString().replace(',', ' ')}`);
|
||||
} else {
|
||||
const IDsToDelete = (
|
||||
await execShellAsync(
|
||||
`docker images ls --filter=reference='${name}' --filter=before='${name}:${tag}' --format '{{json .ID }}'`
|
||||
)
|
||||
)
|
||||
.trim()
|
||||
.replace(/"/g, '')
|
||||
.split('\n');
|
||||
if (IDsToDelete.length > 1)
|
||||
await execShellAsync(`docker rmi -f ${IDsToDelete.toString().replace(',', ' ')}`);
|
||||
try {
|
||||
await execShellAsync('docker container prune -f');
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
try {
|
||||
if (deleteAll) {
|
||||
const IDsToDelete = (
|
||||
await execShellAsync(
|
||||
`docker images ls --filter=reference='${name}' --format '{{json .ID }}'`
|
||||
)
|
||||
)
|
||||
.trim()
|
||||
.replace(/"/g, '')
|
||||
.split('\n');
|
||||
if (IDsToDelete.length > 0) await execShellAsync(`docker rmi -f ${IDsToDelete.join(' ')}`);
|
||||
} else {
|
||||
const IDsToDelete = (
|
||||
await execShellAsync(
|
||||
`docker images ls --filter=reference='${name}' --filter=before='${name}:${tag}' --format '{{json .ID }}'`
|
||||
)
|
||||
)
|
||||
.trim()
|
||||
.replace(/"/g, '')
|
||||
.split('\n');
|
||||
if (IDsToDelete.length > 1) await execShellAsync(`docker rmi -f ${IDsToDelete.join(' ')}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
try {
|
||||
await execShellAsync('docker image prune -f');
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
await execShellAsync('docker image prune -f');
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { execShellAsync } from '../common';
|
||||
export default async function (configuration) {
|
||||
try {
|
||||
const { GITHUB_APP_PRIVATE_KEY } = process.env;
|
||||
const { workdir } = configuration.general;
|
||||
const { workdir, isPreviewDeploymentEnabled, pullRequest } = configuration.general;
|
||||
const { organization, name, branch } = configuration.repository;
|
||||
const github = configuration.github;
|
||||
if (!github.installation.id || !github.app.id) {
|
||||
@@ -37,8 +37,12 @@ export default async function (configuration) {
|
||||
await execShellAsync(
|
||||
`mkdir -p ${workdir} && git clone -q -b ${branch} https://x-access-token:${token}@github.com/${organization}/${name}.git ${workdir}/`
|
||||
);
|
||||
|
||||
if (isPreviewDeploymentEnabled && pullRequest && pullRequest !== 0) {
|
||||
await execShellAsync(`cd ${workdir} && git fetch origin pull/${pullRequest}/head:pull_${pullRequest} && git checkout pull_${pullRequest}`)
|
||||
}
|
||||
configuration.build.container.tag = (
|
||||
await execShellAsync(`cd ${configuration.general.workdir}/ && git rev-parse HEAD`)
|
||||
await execShellAsync(`cd ${workdir}/ && git rev-parse HEAD`)
|
||||
)
|
||||
.replace('\n', '')
|
||||
.slice(0, 7);
|
||||
|
||||
@@ -10,9 +10,8 @@ function getUniq() {
|
||||
}
|
||||
|
||||
export function setDefaultConfiguration(configuration) {
|
||||
const nickname = getUniq();
|
||||
const nickname = configuration.general.nickname || getUniq();
|
||||
const deployId = cuid();
|
||||
|
||||
const shaBase = JSON.stringify({ repository: configuration.repository });
|
||||
const sha256 = crypto.createHash('sha256').update(shaBase).digest('hex');
|
||||
|
||||
@@ -21,17 +20,22 @@ export function setDefaultConfiguration(configuration) {
|
||||
configuration.general.nickname = nickname;
|
||||
configuration.general.deployId = deployId;
|
||||
configuration.general.workdir = `/tmp/${deployId}`;
|
||||
|
||||
if (configuration.general.isPreviewDeploymentEnabled && configuration.general.pullRequest !== 0) {
|
||||
configuration.build.container.name = `pr${configuration.general.pullRequest}-${sha256.slice(0, 8)}`
|
||||
configuration.publish.domain = `pr${configuration.general.pullRequest}.${configuration.publish.domain}`
|
||||
}
|
||||
if (!configuration.publish.path) configuration.publish.path = '/';
|
||||
if (!configuration.publish.port) {
|
||||
if (
|
||||
configuration.build.pack === 'nodejs' ||
|
||||
configuration.build.pack === 'vuejs' ||
|
||||
configuration.build.pack === 'nuxtjs' ||
|
||||
configuration.build.pack === 'rust' ||
|
||||
configuration.build.pack === 'nextjs'
|
||||
configuration.build.pack === 'nextjs' ||
|
||||
configuration.build.pack === 'nestjs'
|
||||
) {
|
||||
configuration.publish.port = 3000;
|
||||
} else if (configuration.build.pack === 'python') {
|
||||
configuration.publish.port = 4000;
|
||||
} else {
|
||||
configuration.publish.port = 80;
|
||||
}
|
||||
@@ -48,6 +52,19 @@ export function setDefaultConfiguration(configuration) {
|
||||
if (!configuration.build.command.installation)
|
||||
configuration.build.command.installation = 'yarn install';
|
||||
}
|
||||
if (
|
||||
configuration.build.pack === 'nodejs' ||
|
||||
configuration.build.pack === 'vuejs' ||
|
||||
configuration.build.pack === 'nuxtjs' ||
|
||||
configuration.build.pack === 'nextjs' ||
|
||||
configuration.build.pack === 'nestjs'
|
||||
) {
|
||||
if (!configuration.build.command.start) configuration.build.command.start = 'yarn start'
|
||||
}
|
||||
if (configuration.build.pack === 'python') {
|
||||
if (!configuration.build.command.python.module) configuration.build.command.python.module = 'main'
|
||||
if (!configuration.build.command.python.instance) configuration.build.command.python.instance = 'app'
|
||||
}
|
||||
|
||||
configuration.build.container.baseSHA = crypto
|
||||
.createHash('sha256')
|
||||
@@ -58,20 +75,23 @@ export function setDefaultConfiguration(configuration) {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
export async function precheckDeployment({ services, configuration }) {
|
||||
export async function precheckDeployment(configuration) {
|
||||
const services = (await docker.engine.listServices()).filter(
|
||||
(r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application' && JSON.parse(r.Spec.Labels.configuration).publish.domain === configuration.publish.domain
|
||||
);
|
||||
let foundService = false;
|
||||
let configChanged = false;
|
||||
let imageChanged = false;
|
||||
|
||||
let forceUpdate = false;
|
||||
|
||||
for (const service of services) {
|
||||
const running = JSON.parse(service.Spec.Labels.configuration);
|
||||
if (running) {
|
||||
|
||||
if (
|
||||
running.repository.id === configuration.repository.id &&
|
||||
running.repository.branch === configuration.repository.branch
|
||||
) {
|
||||
foundService = true;
|
||||
// Base service configuration changed
|
||||
if (
|
||||
!running.build.container.baseSHA ||
|
||||
@@ -91,9 +111,32 @@ export async function precheckDeployment({ services, configuration }) {
|
||||
(n) =>
|
||||
n.DesiredState !== 'Running' && n.Image.split(':')[1] === running.build.container.tag
|
||||
);
|
||||
if (isError.length > 0) forceUpdate = true;
|
||||
foundService = true;
|
||||
if (isError.length > 0) {
|
||||
forceUpdate = true;
|
||||
}
|
||||
|
||||
const compareObjects = (a, b) => {
|
||||
if (a === b) return true;
|
||||
|
||||
if (typeof a != 'object' || typeof b != 'object' || a == null || b == null) return false;
|
||||
|
||||
let keysA = Object.keys(a), keysB = Object.keys(b);
|
||||
|
||||
if (keysA.length != keysB.length) return false;
|
||||
|
||||
for (let key of keysA) {
|
||||
if (!keysB.includes(key)) return false;
|
||||
|
||||
if (typeof a[key] === 'function' || typeof b[key] === 'function') {
|
||||
if (a[key].toString() != b[key].toString()) return false;
|
||||
} else {
|
||||
if (!compareObjects(a[key], b[key])) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const runningWithoutContainer = JSON.parse(JSON.stringify(running));
|
||||
delete runningWithoutContainer.build.container;
|
||||
|
||||
@@ -102,16 +145,19 @@ export async function precheckDeployment({ services, configuration }) {
|
||||
|
||||
// If only the configuration changed
|
||||
if (
|
||||
JSON.stringify(runningWithoutContainer.build) !==
|
||||
JSON.stringify(configurationWithoutContainer.build) ||
|
||||
JSON.stringify(runningWithoutContainer.publish) !==
|
||||
JSON.stringify(configurationWithoutContainer.publish)
|
||||
)
|
||||
!compareObjects(runningWithoutContainer.build,configurationWithoutContainer.build) ||
|
||||
!compareObjects(runningWithoutContainer.publish,configurationWithoutContainer.publish) ||
|
||||
runningWithoutContainer.general.isPreviewDeploymentEnabled !==
|
||||
configurationWithoutContainer.general.isPreviewDeploymentEnabled
|
||||
){
|
||||
configChanged = true;
|
||||
}
|
||||
|
||||
// If only the image changed
|
||||
if (running.build.container.tag !== configuration.build.container.tag) imageChanged = true;
|
||||
// If build pack changed, forceUpdate the service
|
||||
if (running.build.pack !== configuration.build.pack) forceUpdate = true;
|
||||
if (configuration.general.isPreviewDeploymentEnabled && configuration.general.pullRequest !== 0) forceUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,29 +174,5 @@ export async function precheckDeployment({ services, configuration }) {
|
||||
}
|
||||
|
||||
export async function updateServiceLabels(configuration) {
|
||||
// In case of any failure during deployment, still update the current configuration.
|
||||
const services = (await docker.engine.listServices()).filter(
|
||||
(r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application'
|
||||
);
|
||||
const found = services.find((s) => {
|
||||
const config = JSON.parse(s.Spec.Labels.configuration);
|
||||
if (
|
||||
config.repository.id === configuration.repository.id &&
|
||||
config.repository.branch === configuration.repository.branch
|
||||
) {
|
||||
return config;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
if (found) {
|
||||
const { ID } = found;
|
||||
const Labels = { ...JSON.parse(found.Spec.Labels.configuration), ...configuration };
|
||||
await execShellAsync(
|
||||
`docker service update --label-add configuration='${JSON.stringify(
|
||||
Labels
|
||||
)}' --label-add com.docker.stack.image='${configuration.build.container.name}:${
|
||||
configuration.build.container.tag
|
||||
}' ${ID}`
|
||||
);
|
||||
}
|
||||
return await execShellAsync(`docker service update --label-add configuration='${JSON.stringify(configuration)}' ${configuration.build.container.name}_${configuration.build.container.name}`)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { docker } from '$lib/api/docker';
|
||||
import { saveAppLog } from './logging';
|
||||
import { promises as fs } from 'fs';
|
||||
import { deleteSameDeployments } from './cleanup';
|
||||
import { deleteSameDeployments, purgeImagesContainers } from './cleanup';
|
||||
import yaml from 'js-yaml';
|
||||
import { execShellAsync } from '../common';
|
||||
import { delay, execShellAsync } from '../common';
|
||||
|
||||
export default async function (configuration, imageChanged) {
|
||||
const generateEnvs = {};
|
||||
@@ -11,6 +11,7 @@ export default async function (configuration, imageChanged) {
|
||||
generateEnvs[secret.name] = secret.value;
|
||||
}
|
||||
const containerName = configuration.build.container.name;
|
||||
const containerTag = configuration.build.container.tag;
|
||||
|
||||
// Only save SHA256 of it in the configuration label
|
||||
const baseServiceConfiguration = configuration.baseServiceConfiguration;
|
||||
@@ -20,7 +21,7 @@ export default async function (configuration, imageChanged) {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[containerName]: {
|
||||
image: `${configuration.build.container.name}:${configuration.build.container.tag}`,
|
||||
image: `${containerName}:${containerTag}`,
|
||||
networks: [`${docker.network}`],
|
||||
environment: generateEnvs,
|
||||
deploy: {
|
||||
@@ -31,21 +32,21 @@ export default async function (configuration, imageChanged) {
|
||||
'configuration=' + JSON.stringify(configuration),
|
||||
'traefik.enable=true',
|
||||
'traefik.http.services.' +
|
||||
configuration.build.container.name +
|
||||
containerName +
|
||||
`.loadbalancer.server.port=${configuration.publish.port}`,
|
||||
'traefik.http.routers.' + configuration.build.container.name + '.entrypoints=websecure',
|
||||
'traefik.http.routers.' + containerName + '.entrypoints=websecure',
|
||||
'traefik.http.routers.' +
|
||||
configuration.build.container.name +
|
||||
containerName +
|
||||
'.rule=Host(`' +
|
||||
configuration.publish.domain +
|
||||
'`) && PathPrefix(`' +
|
||||
configuration.publish.path +
|
||||
'`)',
|
||||
'traefik.http.routers.' +
|
||||
configuration.build.container.name +
|
||||
containerName +
|
||||
'.tls.certresolver=letsencrypt',
|
||||
'traefik.http.routers.' +
|
||||
configuration.build.container.name +
|
||||
containerName +
|
||||
'.middlewares=global-compress'
|
||||
]
|
||||
}
|
||||
@@ -62,7 +63,7 @@ export default async function (configuration, imageChanged) {
|
||||
if (imageChanged) {
|
||||
// console.log('image changed')
|
||||
await execShellAsync(
|
||||
`docker service update --image ${configuration.build.container.name}:${configuration.build.container.tag} ${configuration.build.container.name}_${configuration.build.container.name}`
|
||||
`docker service update --image ${containerName}:${containerTag} ${containerName}_${containerName}`
|
||||
);
|
||||
} else {
|
||||
// console.log('new deployment or force deployment or config changed')
|
||||
@@ -71,6 +72,11 @@ export default async function (configuration, imageChanged) {
|
||||
`cat ${configuration.general.workdir}/stack.yml | docker stack deploy --prune -c - ${containerName}`
|
||||
);
|
||||
}
|
||||
async function purgeImagesAsync(found) {
|
||||
await delay(10000);
|
||||
await purgeImagesContainers(found, true);
|
||||
}
|
||||
purgeImagesAsync(configuration)
|
||||
|
||||
await saveAppLog('### Published done!', configuration);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import Settings from '$models/Settings';
|
||||
import ServerLog from '$models/Logs/Server';
|
||||
import ApplicationLog from '$models/Logs/Application';
|
||||
import ServerLog from '$models/ServerLog';
|
||||
import ApplicationLog from '$models/ApplicationLog';
|
||||
import dayjs from 'dayjs';
|
||||
import { version } from '../../../../package.json';
|
||||
|
||||
|
||||
function generateTimestamp() {
|
||||
return `${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} `;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { docker, streamEvents } from '$lib/api/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const buildImageNodeDocker = (configuration) => {
|
||||
const buildImageNodeDocker = (configuration, prodBuild) => {
|
||||
return [
|
||||
'FROM node:lts',
|
||||
'WORKDIR /usr/src/app',
|
||||
`COPY ${configuration.build.directory}/package*.json ./`,
|
||||
configuration.build.command.installation && `RUN ${configuration.build.command.installation}`,
|
||||
`COPY ./${configuration.build.directory} ./`,
|
||||
`RUN ${configuration.build.command.build}`
|
||||
`RUN ${configuration.build.command.build}`,
|
||||
prodBuild && `RUN rm -fr node_modules && ${configuration.build.command.installation} --prod`
|
||||
].join('\n');
|
||||
};
|
||||
export async function buildImage(configuration, cacheBuild?: boolean) {
|
||||
export async function buildImage(configuration, cacheBuild?: boolean, prodBuild?: boolean) {
|
||||
await fs.writeFile(
|
||||
`${configuration.general.workdir}/Dockerfile`,
|
||||
buildImageNodeDocker(configuration)
|
||||
buildImageNodeDocker(configuration, prodBuild)
|
||||
);
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
|
||||
@@ -7,8 +7,10 @@ import php from './php';
|
||||
import nuxtjs from './nuxtjs';
|
||||
import nodejs from './nodejs';
|
||||
import nextjs from './nextjs';
|
||||
import nestjs from './nestjs';
|
||||
import gatsby from './gatsby';
|
||||
import docker from './docker';
|
||||
import python from './python';
|
||||
|
||||
export {
|
||||
vuejs,
|
||||
@@ -20,6 +22,8 @@ export {
|
||||
nuxtjs,
|
||||
nodejs,
|
||||
nextjs,
|
||||
nestjs,
|
||||
gatsby,
|
||||
docker
|
||||
docker,
|
||||
python
|
||||
};
|
||||
|
||||
31
src/lib/api/applications/packs/nestjs/index.ts
Normal file
31
src/lib/api/applications/packs/nestjs/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { docker, streamEvents } from '$lib/api/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
import { buildImage } from '../helpers';
|
||||
// `HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost:${configuration.publish.port}${configuration.publish.path} || exit 1`,
|
||||
const publishNodejsDocker = (configuration) => {
|
||||
return [
|
||||
'FROM node:lts',
|
||||
'WORKDIR /usr/src/app',
|
||||
configuration.build.command.build
|
||||
? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag} /usr/src/app/${configuration.publish.directory} ./`
|
||||
: `
|
||||
COPY ${configuration.build.directory}/package*.json ./
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
`CMD ${configuration.build.command.start}`
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
export default async function (configuration) {
|
||||
if (configuration.build.command.build) await buildImage(configuration, false, true);
|
||||
await fs.writeFile(
|
||||
`${configuration.general.workdir}/Dockerfile`,
|
||||
publishNodejsDocker(configuration)
|
||||
);
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
);
|
||||
await streamEvents(stream, configuration);
|
||||
}
|
||||
@@ -13,7 +13,7 @@ const publishNodejsDocker = (configuration) => {
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
'CMD [ "yarn", "start" ]'
|
||||
`CMD ${configuration.build.command.start}`
|
||||
].join('\n');
|
||||
};
|
||||
export default async function (configuration) {
|
||||
|
||||
@@ -13,7 +13,7 @@ const publishNodejsDocker = (configuration) => {
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
'CMD [ "yarn", "start" ]'
|
||||
`CMD ${configuration.build.command.start}`
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const publishNodejsDocker = (configuration) => {
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
'CMD [ "yarn", "start" ]'
|
||||
`CMD ${configuration.build.command.start}`
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ const publishPHPDocker = (configuration) => {
|
||||
'WORKDIR /usr/src/app',
|
||||
`COPY ./${configuration.build.directory} /var/www/html`,
|
||||
'EXPOSE 80',
|
||||
' CMD ["apache2-foreground"]'
|
||||
'CMD ["apache2-foreground"]'
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
|
||||
27
src/lib/api/applications/packs/python/index.ts
Normal file
27
src/lib/api/applications/packs/python/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { docker, streamEvents } from '$lib/api/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
// `HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost:${configuration.publish.port}${configuration.publish.path} || exit 1`,
|
||||
const publishPython = (configuration) => {
|
||||
return [
|
||||
'FROM python:3-alpine',
|
||||
'WORKDIR /usr/src/app',
|
||||
'RUN pip install gunicorn',
|
||||
`COPY ./${configuration.build.directory}/requirements.txt ./`,
|
||||
`RUN pip install --no-cache-dir -r ./${configuration.build.directory}/requirements.txt`,
|
||||
`COPY ./${configuration.build.directory}/ .`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
`CMD gunicorn -w=4 ${configuration.build.command.python.module}:${configuration.build.command.python.instance}`
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
export default async function (configuration) {
|
||||
await fs.writeFile(
|
||||
`${configuration.general.workdir}/Dockerfile`,
|
||||
publishPython(configuration)
|
||||
);
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
);
|
||||
await streamEvents(stream, configuration);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
const defaultBuildAndDeploy = {
|
||||
installation: 'yarn install',
|
||||
build: 'yarn build'
|
||||
build: 'yarn build',
|
||||
start: 'yarn start'
|
||||
};
|
||||
|
||||
const templates = {
|
||||
@@ -10,6 +11,13 @@ const templates = {
|
||||
directory: 'public',
|
||||
name: 'Svelte'
|
||||
},
|
||||
'@nestjs/core': {
|
||||
pack: 'nestjs',
|
||||
...defaultBuildAndDeploy,
|
||||
start: 'yarn start:prod',
|
||||
port: 3000,
|
||||
name: 'NestJS'
|
||||
},
|
||||
next: {
|
||||
pack: 'nextjs',
|
||||
...defaultBuildAndDeploy,
|
||||
@@ -38,6 +46,7 @@ const templates = {
|
||||
pack: 'vuejs',
|
||||
...defaultBuildAndDeploy,
|
||||
directory: 'dist',
|
||||
port: 80,
|
||||
name: 'Vue'
|
||||
},
|
||||
gatsby: {
|
||||
@@ -1,4 +1,5 @@
|
||||
import Deployment from '$models/Logs/Deployment';
|
||||
|
||||
import Deployment from '$models/Deployment';
|
||||
import dayjs from 'dayjs';
|
||||
import buildContainer from './buildContainer';
|
||||
import { updateServiceLabels } from './configuration';
|
||||
@@ -9,23 +10,22 @@ import { saveAppLog } from './logging';
|
||||
export default async function (configuration, imageChanged) {
|
||||
const { id, organization, name, branch } = configuration.repository;
|
||||
const { domain } = configuration.publish;
|
||||
const { deployId, nickname } = configuration.general;
|
||||
await new Deployment({
|
||||
repoId: id,
|
||||
branch,
|
||||
deployId,
|
||||
domain,
|
||||
organization,
|
||||
name,
|
||||
nickname
|
||||
}).save();
|
||||
await saveAppLog(`${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} Queued.`, configuration);
|
||||
await copyFiles(configuration);
|
||||
await buildContainer(configuration);
|
||||
await deploy(configuration, imageChanged);
|
||||
await Deployment.findOneAndUpdate(
|
||||
{ repoId: id, branch, deployId, organization, name, domain },
|
||||
{ repoId: id, branch, deployId, organization, name, domain, progress: 'done' }
|
||||
);
|
||||
await updateServiceLabels(configuration);
|
||||
const { deployId } = configuration.general;
|
||||
try {
|
||||
await saveAppLog(`${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} Queued.`, configuration);
|
||||
await copyFiles(configuration);
|
||||
await buildContainer(configuration);
|
||||
await deploy(configuration, imageChanged);
|
||||
await Deployment.findOneAndUpdate(
|
||||
{ repoId: id, branch, deployId, organization, name, domain },
|
||||
{ repoId: id, branch, deployId, organization, name, domain, progress: 'done' }
|
||||
);
|
||||
|
||||
await updateServiceLabels(configuration);
|
||||
} catch (error) {
|
||||
await Deployment.findOneAndUpdate(
|
||||
{ repoId: id, branch, deployId, organization, name, domain },
|
||||
{ repoId: id, branch, deployId, organization, name, domain, progress: 'failed' }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import shell from 'shelljs';
|
||||
import User from '$models/User';
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
import { saveServerLog } from './applications/logging';
|
||||
|
||||
export function execShellAsync(cmd, opts = {}) {
|
||||
try {
|
||||
return new Promise(function (resolve, reject) {
|
||||
shell.config.silent = true;
|
||||
shell.exec(cmd, opts, function (code, stdout, stderr) {
|
||||
if (code !== 0) return reject(new Error(stderr));
|
||||
shell.exec(cmd, opts, async function (code, stdout, stderr) {
|
||||
if (code !== 0) {
|
||||
await saveServerLog({ message: JSON.stringify({ cmd, opts, code, stdout, stderr }) })
|
||||
return reject(new Error(stderr));
|
||||
}
|
||||
return resolve(stdout);
|
||||
});
|
||||
});
|
||||
|
||||
23
src/lib/api/github.ts
Normal file
23
src/lib/api/github.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
|
||||
export async function githubAPI(
|
||||
request: Request,
|
||||
resource: string,
|
||||
token?: string,
|
||||
data?: Record<string, unknown>
|
||||
) {
|
||||
const base = 'https://api.github.com';
|
||||
const res = await fetch(`${base}${resource}`, {
|
||||
method: request.method,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json',
|
||||
authorization: token ? `token ${token}` : ''
|
||||
},
|
||||
body: data && JSON.stringify(data)
|
||||
});
|
||||
return {
|
||||
status: res.status,
|
||||
body: await res.json()
|
||||
};
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export const publicPages = ['/', '/api/v1/login/github/app', '/api/v1/webhooks/deploy', '/success'];
|
||||
export const VITE_GITHUB_APP_NAME = import.meta.env.VITE_GITHUB_APP_NAME
|
||||
|
||||
@@ -61,10 +61,8 @@ export async function request(
|
||||
} else if (response.headers.get('content-type').match(/multipart\/form-data/)) {
|
||||
return await response.formData();
|
||||
} else {
|
||||
console.log(response);
|
||||
if (response.headers.get('content-disposition')) {
|
||||
const blob = await response.blob();
|
||||
console.log(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = response.headers.get('content-disposition').split('=')[1] || 'backup.gz';
|
||||
@@ -86,10 +84,10 @@ export async function request(
|
||||
});
|
||||
} else if (response.status >= 500) {
|
||||
const error = (await response.json()).error;
|
||||
browser && toast.push(error);
|
||||
browser && toast.push(error.message || error);
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
error: error || 'Oops, something is not okay. Are you okay?'
|
||||
error: error.message || error || 'Oops, something is not okay. Are you okay?'
|
||||
});
|
||||
} else {
|
||||
browser && toast.push(response.statusText);
|
||||
@@ -8,4 +8,4 @@ const ApplicationLogsSchema = new Schema({
|
||||
|
||||
ApplicationLogsSchema.set('timestamps', true);
|
||||
|
||||
export default mongoose.model('logs-application', ApplicationLogsSchema);
|
||||
export default mongoose.models['logs-application'] || mongoose.model('logs-application', ApplicationLogsSchema);
|
||||
55
src/models/Configuration.ts
Normal file
55
src/models/Configuration.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import mongoose from 'mongoose';
|
||||
const { Schema } = mongoose;
|
||||
const ConfigurationSchema = new Schema({
|
||||
github: {
|
||||
installation: {
|
||||
id: { type: Number, required: true }
|
||||
},
|
||||
app: {
|
||||
id: { type: Number, required: true }
|
||||
}
|
||||
},
|
||||
repository: {
|
||||
id: { type: Number, required: true },
|
||||
organization: { type: String, required: true },
|
||||
name: { type: String, required: true },
|
||||
branch: { type: String, required: true }
|
||||
},
|
||||
general: {
|
||||
deployId: { type: String, required: true },
|
||||
nickname: { type: String, required: true },
|
||||
workdir: { type: String, required: true },
|
||||
isPreviewDeploymentEnabled: { type: Boolean, required: true, default: false },
|
||||
pullRequest: { type: Number, required: true, default: 0 },
|
||||
},
|
||||
build: {
|
||||
pack: { type: String, required: true },
|
||||
directory: { type: String },
|
||||
command: {
|
||||
build: { type: String },
|
||||
installation: { type: String },
|
||||
start: { type: String },
|
||||
python: {
|
||||
module: { type: String },
|
||||
instance: { type: String },
|
||||
}
|
||||
|
||||
},
|
||||
container: {
|
||||
name: { type: String, required: true },
|
||||
tag: { type: String, required: true },
|
||||
baseSHA: { type: String, required: true },
|
||||
},
|
||||
},
|
||||
publish: {
|
||||
directory: { type: String },
|
||||
domain: { type: String, required: true },
|
||||
path: { type: String },
|
||||
port: { type: Number },
|
||||
secrets: { type: Array },
|
||||
}
|
||||
});
|
||||
|
||||
ConfigurationSchema.set('timestamps', true);
|
||||
|
||||
export default mongoose.models['configuration'] || mongoose.model('configuration', ConfigurationSchema);
|
||||
@@ -14,4 +14,4 @@ const DeploymentSchema = new Schema({
|
||||
|
||||
DeploymentSchema.set('timestamps', true);
|
||||
|
||||
export default mongoose.model('deployment', DeploymentSchema);
|
||||
export default mongoose.models['deployment'] || mongoose.model('deployment', DeploymentSchema);
|
||||
@@ -1,5 +1,5 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { version } from '../../../package.json';
|
||||
import { version } from '../../package.json'
|
||||
const { Schema, Document } = mongoose;
|
||||
|
||||
// export interface ILogsServer extends Document {
|
||||
@@ -20,4 +20,4 @@ const LogsServerSchema = new Schema({
|
||||
|
||||
LogsServerSchema.set('timestamps', { createdAt: 'createdAt', updatedAt: false });
|
||||
|
||||
export default mongoose.model('logs-server', LogsServerSchema);
|
||||
export default mongoose.models['logs-server'] || mongoose.model('logs-server', LogsServerSchema);
|
||||
@@ -14,4 +14,4 @@ const SettingsSchema = new Schema({
|
||||
|
||||
SettingsSchema.set('timestamps', true);
|
||||
|
||||
export default mongoose.model('settings', SettingsSchema);
|
||||
export default mongoose.models['settings'] || mongoose.model('settings', SettingsSchema);
|
||||
|
||||
@@ -14,4 +14,4 @@ const UserSchema = new Schema({
|
||||
|
||||
UserSchema.set('timestamps', true);
|
||||
|
||||
export default mongoose.model('user', UserSchema);
|
||||
export default mongoose.models['user'] || mongoose.model('user', UserSchema);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script context="module" lang="ts">
|
||||
import { publicPages } from '$lib/consts';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
/**
|
||||
* @type {import('@sveltejs/kit').Load}
|
||||
*/
|
||||
@@ -306,7 +306,7 @@
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="opacity-50 tracking-tight font-bold text-xs rounded px-2 cursor-not-allowed"
|
||||
class="opacity-50 tracking-tight font-bold text-xs rounded px-2 cursor-not-allowed"
|
||||
disabled={upgradeDisabled}>Upgrading. It could take a while, please wait...</button
|
||||
>
|
||||
{/if}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
|
||||
// export async function api(request: Request, resource: string, data?: {}) {
|
||||
// const base = 'https://github.com/';
|
||||
// if (!request.context.isLoggedIn) {
|
||||
// return { status: 401, body: 'Unauthorized' };
|
||||
// }
|
||||
|
||||
// const res = await fetch(`${base}${resource}`, {
|
||||
// method: request.method,
|
||||
// headers: {
|
||||
// 'content-type': 'application/json'
|
||||
// },
|
||||
// body: data && JSON.stringify(data)
|
||||
// });
|
||||
// return {
|
||||
// status: res.status,
|
||||
// body: await res.json()
|
||||
// };
|
||||
// }
|
||||
|
||||
export async function githubAPI(
|
||||
request: Request,
|
||||
resource: string,
|
||||
token?: string,
|
||||
data?: Record<string, unknown>
|
||||
) {
|
||||
const base = 'https://api.github.com';
|
||||
const res = await fetch(`${base}${resource}`, {
|
||||
method: request.method,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json',
|
||||
authorization: token ? `token ${token}` : ''
|
||||
},
|
||||
body: data && JSON.stringify(data)
|
||||
});
|
||||
return {
|
||||
status: res.status,
|
||||
body: await res.json()
|
||||
};
|
||||
}
|
||||
@@ -1,32 +1,18 @@
|
||||
import { setDefaultConfiguration } from '$lib/api/applications/configuration';
|
||||
import { saveServerLog } from '$lib/api/applications/logging';
|
||||
import { docker } from '$lib/api/docker';
|
||||
import Configuration from '$models/Configuration';
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
|
||||
export async function post(request: Request) {
|
||||
try {
|
||||
const { DOMAIN } = process.env;
|
||||
const configuration = setDefaultConfiguration(request.body);
|
||||
|
||||
const services = (await docker.engine.listServices()).filter(
|
||||
(r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application'
|
||||
);
|
||||
let foundDomain = false;
|
||||
|
||||
for (const service of services) {
|
||||
const running = JSON.parse(service.Spec.Labels.configuration);
|
||||
if (running) {
|
||||
if (
|
||||
running.publish.domain === configuration.publish.domain &&
|
||||
running.repository.id !== configuration.repository.id &&
|
||||
running.publish.path === configuration.publish.path
|
||||
) {
|
||||
foundDomain = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DOMAIN === configuration.publish.domain) foundDomain = true;
|
||||
if (foundDomain) {
|
||||
const configurationFound = await Configuration.find({
|
||||
'repository.id': { '$ne': configuration.repository.id },
|
||||
'publish.domain': configuration.publish.domain
|
||||
}).select('-_id -__v -createdAt -updatedAt')
|
||||
if (configurationFound.length > 0 || configuration.publish.domain === DOMAIN) {
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
@@ -38,13 +24,13 @@ export async function post(request: Request) {
|
||||
return {
|
||||
status: 200,
|
||||
body: { success: true, message: 'OK' }
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
await saveServerLog(error);
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
import { docker } from '$lib/api/docker';
|
||||
import Configuration from '$models/Configuration';
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
|
||||
export async function post(request: Request) {
|
||||
const { name, organization, branch }: any = request.body || {};
|
||||
if (name && organization && branch) {
|
||||
const configurationFound = await Configuration.find({
|
||||
'repository.name': name,
|
||||
'repository.organization': organization,
|
||||
'repository.branch': branch
|
||||
}).select('-_id -__v -createdAt -updatedAt')
|
||||
|
||||
if (configurationFound) {
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
configuration: [...configurationFound]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const services = await docker.engine.listServices();
|
||||
const applications = services.filter(
|
||||
(r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application'
|
||||
@@ -30,6 +46,7 @@ export async function post(request: Request) {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (found) {
|
||||
return {
|
||||
status: 200,
|
||||
@@ -38,13 +55,12 @@ export async function post(request: Request) {
|
||||
...JSON.parse(found.Spec.Labels.configuration)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'No configuration found.'
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'No configuration found.'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
62
src/routes/api/v1/application/config/previewDeployment.ts
Normal file
62
src/routes/api/v1/application/config/previewDeployment.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { updateServiceLabels } from '$lib/api/applications/configuration';
|
||||
import { execShellAsync } from '$lib/api/common';
|
||||
import { docker } from '$lib/api/docker';
|
||||
import ApplicationLog from '$models/ApplicationLog';
|
||||
import Configuration from '$models/Configuration';
|
||||
import Deployment from '$models/Deployment';
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
|
||||
export async function post(request: Request) {
|
||||
const { name, organization, branch, isPreviewDeploymentEnabled }: any = request.body || {};
|
||||
if (name && organization && branch) {
|
||||
const configuration = await Configuration.findOneAndUpdate({
|
||||
'repository.name': name,
|
||||
'repository.organization': organization,
|
||||
'repository.branch': branch
|
||||
}, { $set: { 'general.isPreviewDeploymentEnabled': isPreviewDeploymentEnabled, 'general.pullRequest': 0 } }, { new: true }).select('-_id -__v -createdAt -updatedAt')
|
||||
if (!isPreviewDeploymentEnabled) {
|
||||
const found = await Configuration.find({
|
||||
'repository.name': name,
|
||||
'repository.organization': organization,
|
||||
'repository.branch': branch,
|
||||
'general.pullRequest': { '$ne': 0 }
|
||||
})
|
||||
for (const prDeployment of found) {
|
||||
await Configuration.findOneAndRemove({
|
||||
'repository.name': name,
|
||||
'repository.organization': organization,
|
||||
'repository.branch': branch,
|
||||
'publish.domain': prDeployment.publish.domain
|
||||
})
|
||||
const deploys = await Deployment.find({ organization, branch, name, domain: prDeployment.publish.domain });
|
||||
for (const deploy of deploys) {
|
||||
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
||||
await Deployment.deleteMany({ deployId: deploy.deployId });
|
||||
}
|
||||
await execShellAsync(`docker stack rm ${prDeployment.build.container.name}`);
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
organization,
|
||||
name,
|
||||
branch
|
||||
}
|
||||
};
|
||||
}
|
||||
updateServiceLabels(configuration);
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
success: true
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'Cannot save.'
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,31 +1,24 @@
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
import Deployment from '$models/Logs/Deployment';
|
||||
import { docker } from '$lib/api/docker';
|
||||
import Deployment from '$models/Deployment';
|
||||
import { precheckDeployment, setDefaultConfiguration } from '$lib/api/applications/configuration';
|
||||
import cloneRepository from '$lib/api/applications/cloneRepository';
|
||||
import { cleanupTmp } from '$lib/api/common';
|
||||
import queueAndBuild from '$lib/api/applications/queueAndBuild';
|
||||
export async function post(request: Request) {
|
||||
let configuration;
|
||||
try {
|
||||
const services = (await docker.engine.listServices()).filter(
|
||||
(r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application'
|
||||
);
|
||||
configuration = setDefaultConfiguration(request.body);
|
||||
import Configuration from '$models/Configuration';
|
||||
|
||||
if (!configuration) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'Whaaat?'
|
||||
}
|
||||
};
|
||||
}
|
||||
export async function post(request: Request) {
|
||||
const configuration = setDefaultConfiguration(request.body);
|
||||
if (!configuration) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'Whaaat?'
|
||||
}
|
||||
};
|
||||
}
|
||||
try {
|
||||
await cloneRepository(configuration);
|
||||
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment({
|
||||
services,
|
||||
configuration
|
||||
});
|
||||
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(configuration);
|
||||
if (foundService && !forceUpdate && !imageChanged && !configChanged) {
|
||||
cleanupTmp(configuration.general.workdir);
|
||||
return {
|
||||
@@ -53,9 +46,33 @@ export async function post(request: Request) {
|
||||
}
|
||||
};
|
||||
}
|
||||
const { id, organization, name, branch } = configuration.repository;
|
||||
const { domain } = configuration.publish;
|
||||
const { deployId, nickname, pullRequest } = configuration.general;
|
||||
|
||||
await new Deployment({
|
||||
repoId: id,
|
||||
branch,
|
||||
deployId,
|
||||
domain,
|
||||
organization,
|
||||
name,
|
||||
nickname
|
||||
}).save();
|
||||
|
||||
await Configuration.findOneAndUpdate({
|
||||
'repository.id': id,
|
||||
'repository.organization': organization,
|
||||
'repository.name': name,
|
||||
'repository.branch': branch,
|
||||
'general.pullRequest': { '$in': [null, 0] },
|
||||
},
|
||||
{ ...configuration },
|
||||
{ upsert: true, new: true })
|
||||
|
||||
queueAndBuild(configuration, imageChanged);
|
||||
return {
|
||||
status: 200,
|
||||
status: 201,
|
||||
body: {
|
||||
message: 'Deployment queued.',
|
||||
nickname: configuration.general.nickname,
|
||||
@@ -64,26 +81,28 @@ export async function post(request: Request) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
await Deployment.findOneAndUpdate(
|
||||
{
|
||||
repoId: configuration.repository.id,
|
||||
branch: configuration.repository.branch,
|
||||
organization: configuration.repository.organization,
|
||||
name: configuration.repository.name,
|
||||
domain: configuration.publish.domain,
|
||||
domain: configuration.publish.domain
|
||||
},
|
||||
{
|
||||
repoId: configuration.repository.id,
|
||||
branch: configuration.repository.branch,
|
||||
organization: configuration.repository.organization,
|
||||
name: configuration.repository.name,
|
||||
domain: configuration.publish.domain, progress: 'failed'
|
||||
domain: configuration.publish.domain,
|
||||
progress: 'failed'
|
||||
}
|
||||
);
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
import ApplicationLog from '$models/Logs/Application';
|
||||
import Deployment from '$models/Logs/Deployment';
|
||||
import ApplicationLog from '$models/ApplicationLog';
|
||||
import Deployment from '$models/Deployment';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export async function get(request: Request) {
|
||||
@@ -12,8 +12,6 @@ export async function get(request: Request) {
|
||||
|
||||
const deploy: any = await Deployment.findOne({ deployId })
|
||||
.select('-_id -__v')
|
||||
.sort({ createdAt: 'desc' });
|
||||
|
||||
const finalLogs: any = {};
|
||||
finalLogs.progress = deploy.progress;
|
||||
finalLogs.events = logs.map((log) => log.event);
|
||||
@@ -24,11 +22,11 @@ export async function get(request: Request) {
|
||||
...finalLogs
|
||||
}
|
||||
};
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: e
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Request } from '@sveltejs/kit';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc.js';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime.js';
|
||||
import Deployment from '$models/Logs/Deployment';
|
||||
import Deployment from '$models/Deployment';
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(relativeTime);
|
||||
export async function get(request: Request) {
|
||||
@@ -10,22 +10,18 @@ export async function get(request: Request) {
|
||||
const repoId = request.query.get('repoId');
|
||||
const branch = request.query.get('branch');
|
||||
const page = request.query.get('page');
|
||||
|
||||
const onePage = 5;
|
||||
const show = Number(page) * onePage || 5;
|
||||
const deploy: any = await Deployment.find({ repoId, branch })
|
||||
.select('-_id -__v -repoId')
|
||||
.sort({ createdAt: 'desc' })
|
||||
.limit(show);
|
||||
|
||||
const finalLogs = deploy.map((d) => {
|
||||
const finalLogs = { ...d._doc };
|
||||
|
||||
const updatedAt = dayjs(d.updatedAt).utc();
|
||||
|
||||
finalLogs.took = updatedAt.diff(dayjs(d.createdAt)) / 1000;
|
||||
finalLogs.since = updatedAt.fromNow();
|
||||
|
||||
finalLogs.isPr = d.domain.startsWith('pr')
|
||||
return finalLogs;
|
||||
});
|
||||
return {
|
||||
@@ -36,11 +32,10 @@ export async function get(request: Request) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,11 +16,12 @@ export async function get(request: Request) {
|
||||
body: { success: true, logs }
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
await saveServerLog(error);
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error
|
||||
error: 'No such service. Is it under deployment?'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,55 +1,69 @@
|
||||
import { purgeImagesContainers } from '$lib/api/applications/cleanup';
|
||||
import { docker } from '$lib/api/docker';
|
||||
import Deployment from '$models/Logs/Deployment';
|
||||
import ApplicationLog from '$models/Logs/Application';
|
||||
import Deployment from '$models/Deployment';
|
||||
import ApplicationLog from '$models/ApplicationLog';
|
||||
import { delay, execShellAsync } from '$lib/api/common';
|
||||
import Configuration from '$models/Configuration';
|
||||
|
||||
async function call(found) {
|
||||
async function purgeImagesAsync(found) {
|
||||
await delay(10000);
|
||||
await purgeImagesContainers(found, true);
|
||||
}
|
||||
export async function post(request: Request) {
|
||||
const { organization, name, branch } = request.body;
|
||||
let found = false;
|
||||
const { organization, name, branch, domain } = request.body;
|
||||
try {
|
||||
(await docker.engine.listServices())
|
||||
.filter((r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application')
|
||||
.map((s) => {
|
||||
const running = JSON.parse(s.Spec.Labels.configuration);
|
||||
if (
|
||||
running.repository.organization === organization &&
|
||||
running.repository.name === name &&
|
||||
running.repository.branch === branch
|
||||
) {
|
||||
found = running;
|
||||
const configurationFound = await Configuration.findOne({
|
||||
'repository.organization': organization,
|
||||
'repository.name': name,
|
||||
'repository.branch': branch,
|
||||
'publish.domain': domain
|
||||
})
|
||||
if (configurationFound) {
|
||||
const id = configurationFound._id
|
||||
if (configurationFound?.general?.pullRequest === 0) {
|
||||
// Main deployment deletion request; deleting main + PRs
|
||||
const allConfiguration = await Configuration.find({
|
||||
'repository.name': name,
|
||||
'repository.organization': organization,
|
||||
'repository.branch': branch,
|
||||
})
|
||||
for (const config of allConfiguration) {
|
||||
await Configuration.findOneAndRemove({
|
||||
'repository.name': config.repository.name,
|
||||
'repository.organization': config.repository.organization,
|
||||
'repository.branch': config.repository.branch,
|
||||
})
|
||||
await execShellAsync(`docker stack rm ${config.build.container.name}`);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
if (found) {
|
||||
const deploys = await Deployment.find({ organization, branch, name });
|
||||
for (const deploy of deploys) {
|
||||
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
||||
await Deployment.deleteMany({ deployId: deploy.deployId });
|
||||
const deploys = await Deployment.find({ organization, branch, name })
|
||||
for (const deploy of deploys) {
|
||||
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
||||
await Deployment.deleteMany({ deployId: deploy.deployId });
|
||||
}
|
||||
|
||||
purgeImagesAsync(configurationFound);
|
||||
} else {
|
||||
// Delete only PRs
|
||||
await Configuration.findByIdAndRemove(id)
|
||||
await execShellAsync(`docker stack rm ${configurationFound.build.container.name}`);
|
||||
const deploys = await Deployment.find({ organization, branch, name, domain })
|
||||
for (const deploy of deploys) {
|
||||
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
||||
await Deployment.deleteMany({ deployId: deploy.deployId });
|
||||
}
|
||||
purgeImagesAsync(configurationFound);
|
||||
}
|
||||
await execShellAsync(`docker stack rm ${found.build.container.name}`);
|
||||
call(found);
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
organization,
|
||||
name,
|
||||
branch
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: 500,
|
||||
error: {
|
||||
message: 'Nothing to do.'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
organization,
|
||||
name,
|
||||
branch
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return {
|
||||
status: 500,
|
||||
error: {
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
import { docker } from '$lib/api/docker';
|
||||
import LogsServer from '$models/Logs/Server';
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
|
||||
import Configuration from '$models/Configuration'
|
||||
export async function get(request: Request) {
|
||||
const serverLogs = await LogsServer.find();
|
||||
// Should update this to get data from mongodb and update db with the currently running services on start!
|
||||
const dockerServices = await docker.engine.listServices();
|
||||
let applications: any = dockerServices.filter(
|
||||
(r) =>
|
||||
r.Spec.Labels.managedBy === 'coolify' &&
|
||||
r.Spec.Labels.type === 'application' &&
|
||||
r.Spec.Labels.configuration
|
||||
);
|
||||
let databases: any = dockerServices.filter(
|
||||
(r) =>
|
||||
r.Spec.Labels.managedBy === 'coolify' &&
|
||||
@@ -23,15 +16,6 @@ export async function get(request: Request) {
|
||||
r.Spec.Labels.type === 'service' &&
|
||||
r.Spec.Labels.configuration
|
||||
);
|
||||
applications = applications.map((r) => {
|
||||
if (JSON.parse(r.Spec.Labels.configuration)) {
|
||||
return {
|
||||
configuration: JSON.parse(r.Spec.Labels.configuration),
|
||||
UpdatedAt: r.UpdatedAt
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
databases = databases.map((r) => {
|
||||
if (JSON.parse(r.Spec.Labels.configuration)) {
|
||||
return {
|
||||
@@ -49,19 +33,27 @@ export async function get(request: Request) {
|
||||
}
|
||||
return {};
|
||||
});
|
||||
applications = [
|
||||
...new Map(
|
||||
applications.map((item) => [
|
||||
item.configuration.publish.domain + item.configuration.publish.path,
|
||||
item
|
||||
])
|
||||
).values()
|
||||
];
|
||||
const configurations = await Configuration.find({
|
||||
'general.pullRequest': { '$in': [null, 0] }
|
||||
}).select('-_id -__v -createdAt')
|
||||
const applications = []
|
||||
for (const configuration of configurations) {
|
||||
const foundPRDeployments = await Configuration.find({
|
||||
'repository.id': configuration.repository.id,
|
||||
'repository.branch': configuration.repository.branch,
|
||||
'general.pullRequest': { '$ne': 0 }
|
||||
}).select('-_id -__v -createdAt')
|
||||
const payload = {
|
||||
configuration,
|
||||
UpdatedAt: configuration.updatedAt,
|
||||
prBuilds: foundPRDeployments.length > 0 ? true : false,
|
||||
}
|
||||
applications.push(payload)
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
success: true,
|
||||
serverLogs,
|
||||
applications: {
|
||||
deployed: applications
|
||||
},
|
||||
@@ -100,6 +100,31 @@ export async function post(request: Request) {
|
||||
body: fs.readFileSync(`${fullfilename}`)
|
||||
};
|
||||
}
|
||||
} else if (type === 'redis') {
|
||||
if (databaseService) {
|
||||
const password = configuration.database.passwords[0];
|
||||
const databaseName = configuration.database.defaultDatabaseName;
|
||||
const filename = `${databaseName}_${now.getTime()}.rdb`;
|
||||
const fullfilename = `${tmpdir}/${filename}`;
|
||||
await execShellAsync(
|
||||
`docker exec -i ${containerID} /bin/bash -c "redis-cli --pass ${password} save"`
|
||||
);
|
||||
await execShellAsync(
|
||||
`docker cp ${containerID}:/bitnami/redis/data/dump.rdb ${fullfilename}`
|
||||
);
|
||||
await execShellAsync(
|
||||
`docker exec -i ${containerID} /bin/bash -c "rm -f /bitnami/redis/data/dump.rdb"`
|
||||
);
|
||||
return {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Transfer-Encoding': 'binary',
|
||||
'Content-Disposition': `attachment; filename=${filename}`
|
||||
},
|
||||
body: fs.readFileSync(`${fullfilename}`)
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: 501,
|
||||
@@ -108,12 +133,11 @@ export async function post(request: Request) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
await saveServerLog(error);
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
} finally {
|
||||
|
||||
@@ -96,6 +96,12 @@ export async function post(request: Request) {
|
||||
hard: 262144
|
||||
}
|
||||
};
|
||||
} else if (type === 'redis') {
|
||||
image = 'bitnami/redis';
|
||||
volume = `${configuration.general.deployId}-${type}-data:/bitnami/redis/data`;
|
||||
generateEnvs = {
|
||||
REDIS_PASSWORD: passwords[0]
|
||||
};
|
||||
}
|
||||
|
||||
const stack = {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { githubAPI } from '$api';
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
import mongoose from 'mongoose';
|
||||
import User from '$models/User';
|
||||
import Settings from '$models/Settings';
|
||||
import cuid from 'cuid';
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
import { githubAPI } from '$lib/api/github';
|
||||
|
||||
export async function get(request: Request) {
|
||||
const code = request.query.get('code');
|
||||
@@ -17,7 +17,7 @@ export async function get(request: Request) {
|
||||
{ headers: { accept: 'application/json' } }
|
||||
)
|
||||
).json();
|
||||
const { avatar_url, id } = await (await githubAPI(request, '/user', access_token)).body;
|
||||
const { avatar_url } = await (await githubAPI(request, '/user', access_token)).body;
|
||||
const email = (await githubAPI(request, '/user/emails', access_token)).body.filter(
|
||||
(e) => e.primary
|
||||
)[0].email;
|
||||
@@ -41,11 +41,10 @@ export async function get(request: Request) {
|
||||
try {
|
||||
await newUser.save();
|
||||
await defaultSettings.save();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 500,
|
||||
body: e
|
||||
error: error.message || error
|
||||
};
|
||||
}
|
||||
} else {
|
||||
@@ -73,12 +72,11 @@ export async function get(request: Request) {
|
||||
});
|
||||
try {
|
||||
await newUser.save();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: e
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -103,8 +101,6 @@ export async function get(request: Request) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log('error happened');
|
||||
console.log(error);
|
||||
return { status: 500, body: { ...error } };
|
||||
return { status: 500, body: { error: error.message || error } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,12 +34,11 @@ export async function get(request: Request) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
success: false,
|
||||
error
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function get(request: Request) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export async function post(request: Request) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -10,7 +10,6 @@ export async function get(request: Request) {
|
||||
execShellAsync(
|
||||
'docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -u root coolify bash -c "$(curl -fsSL https://get.coollabs.io/coolify/upgrade-p2.sh)"'
|
||||
);
|
||||
// saveServerLog({ message: upgradeP2, type: 'UPGRADE-P-2' })
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
@@ -1,24 +0,0 @@
|
||||
// import { deleteCookies } from '$lib/api/common';
|
||||
// import { verifyUserId } from '$lib/api/common';
|
||||
// import type { Request } from '@sveltejs/kit';
|
||||
// import * as cookie from 'cookie';
|
||||
|
||||
// export async function post(request: Request) {
|
||||
// const { coolToken } = cookie.parse(request.headers.cookie || '');
|
||||
// try {
|
||||
// await verifyUserId(coolToken);
|
||||
// return {
|
||||
// status: 200,
|
||||
// body: { success: true }
|
||||
// };
|
||||
// } catch (error) {
|
||||
// return {
|
||||
// status: 301,
|
||||
// headers: {
|
||||
// location: '/',
|
||||
// 'set-cookie': [...deleteCookies]
|
||||
// },
|
||||
// body: { error: 'Unauthorized' }
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
@@ -1,13 +1,19 @@
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
import crypto from 'crypto';
|
||||
import Deployment from '$models/Logs/Deployment';
|
||||
import Deployment from '$models/Deployment';
|
||||
import { docker } from '$lib/api/docker';
|
||||
import { precheckDeployment, setDefaultConfiguration } from '$lib/api/applications/configuration';
|
||||
import cloneRepository from '$lib/api/applications/cloneRepository';
|
||||
import { cleanupTmp } from '$lib/api/common';
|
||||
import { cleanupTmp, execShellAsync } from '$lib/api/common';
|
||||
import queueAndBuild from '$lib/api/applications/queueAndBuild';
|
||||
import Configuration from '$models/Configuration';
|
||||
import ApplicationLog from '$models/ApplicationLog';
|
||||
import { cleanupStuckedDeploymentsInDB } from '$lib/api/applications/cleanup';
|
||||
export async function post(request: Request) {
|
||||
let configuration;
|
||||
const allowedGithubEvents = ['push', 'pull_request']
|
||||
const allowedPRActions = ['opened', , 'reopened', 'synchronize', 'closed']
|
||||
const githubEvent = request.headers['x-github-event']
|
||||
const { GITHUP_APP_WEBHOOK_SECRET } = process.env;
|
||||
const hmac = crypto.createHmac('sha256', GITHUP_APP_WEBHOOK_SECRET);
|
||||
const digest = Buffer.from(
|
||||
@@ -19,52 +25,92 @@ export async function post(request: Request) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'Invalid request'
|
||||
error: 'Invalid request.'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (request.headers['x-github-event'] !== 'push') {
|
||||
if (!allowedGithubEvents.includes(githubEvent)) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'Not a push event.'
|
||||
error: 'Event not allowed.'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const services = (await docker.engine.listServices()).filter(
|
||||
(r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application'
|
||||
);
|
||||
|
||||
configuration = services.find((r) => {
|
||||
if (request.body.ref.startsWith('refs')) {
|
||||
const branch = request.body.ref.split('/')[2];
|
||||
if (
|
||||
JSON.parse(r.Spec.Labels.configuration).repository.id === request.body.repository.id &&
|
||||
JSON.parse(r.Spec.Labels.configuration).repository.branch === branch
|
||||
) {
|
||||
return r;
|
||||
const applications = await Configuration.find({
|
||||
'repository.id': request.body.repository.id,
|
||||
}).select('-_id -__v -createdAt -updatedAt')
|
||||
if (githubEvent === 'push') {
|
||||
configuration = applications.find((r) => {
|
||||
if (request.body.ref.startsWith('refs')) {
|
||||
if (r.repository.branch === request.body.ref.split('/')[2]) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
} else if (githubEvent === 'pull_request') {
|
||||
if (!allowedPRActions.includes(request.body.action)) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'PR action is not allowed.'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
configuration = setDefaultConfiguration(JSON.parse(configuration.Spec.Labels.configuration));
|
||||
|
||||
configuration = applications.find((r) => r.repository.branch === request.body['pull_request'].base.ref);
|
||||
if (configuration) {
|
||||
if (!configuration.general.isPreviewDeploymentEnabled) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'PR deployments are not enabled.'
|
||||
}
|
||||
};
|
||||
}
|
||||
configuration.general.pullRequest = request.body.number
|
||||
}
|
||||
}
|
||||
if (!configuration) {
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'Whaaat?'
|
||||
error: 'No configuration found.'
|
||||
}
|
||||
};
|
||||
}
|
||||
configuration = setDefaultConfiguration(configuration);
|
||||
const { id, organization, name, branch } = configuration.repository;
|
||||
const { domain } = configuration.publish;
|
||||
const { deployId, nickname, pullRequest } = configuration.general;
|
||||
|
||||
if (request.body.action === 'closed') {
|
||||
const deploys = await Deployment.find({ organization, branch, name, domain });
|
||||
for (const deploy of deploys) {
|
||||
await ApplicationLog.deleteMany({ deployId: deploy.deployId });
|
||||
await Deployment.deleteMany({ deployId: deploy.deployId });
|
||||
}
|
||||
await Configuration.findOneAndRemove({
|
||||
'repository.id': id,
|
||||
'repository.organization': organization,
|
||||
'repository.name': name,
|
||||
'repository.branch': branch,
|
||||
'general.pullRequest': pullRequest
|
||||
})
|
||||
await execShellAsync(`docker stack rm ${configuration.build.container.name}`);
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
success: true,
|
||||
message: 'Removed'
|
||||
}
|
||||
};
|
||||
}
|
||||
await cloneRepository(configuration);
|
||||
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment({
|
||||
services,
|
||||
configuration
|
||||
});
|
||||
const { foundService, imageChanged, configChanged, forceUpdate } = await precheckDeployment(configuration);
|
||||
if (foundService && !forceUpdate && !imageChanged && !configChanged) {
|
||||
cleanupTmp(configuration.general.workdir);
|
||||
return {
|
||||
@@ -76,11 +122,11 @@ export async function post(request: Request) {
|
||||
};
|
||||
}
|
||||
const alreadyQueued = await Deployment.find({
|
||||
repoId: configuration.repository.id,
|
||||
branch: configuration.repository.branch,
|
||||
organization: configuration.repository.organization,
|
||||
name: configuration.repository.name,
|
||||
domain: configuration.publish.domain,
|
||||
repoId: id,
|
||||
branch: branch,
|
||||
organization: organization,
|
||||
name: name,
|
||||
domain: domain,
|
||||
progress: { $in: ['queued', 'inprogress'] }
|
||||
});
|
||||
if (alreadyQueued.length > 0) {
|
||||
@@ -92,6 +138,40 @@ export async function post(request: Request) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
await new Deployment({
|
||||
repoId: id,
|
||||
branch,
|
||||
deployId,
|
||||
domain,
|
||||
organization,
|
||||
name,
|
||||
nickname
|
||||
}).save();
|
||||
|
||||
|
||||
if (githubEvent === 'pull_request') {
|
||||
await Configuration.findOneAndUpdate({
|
||||
'repository.id': id,
|
||||
'repository.organization': organization,
|
||||
'repository.name': name,
|
||||
'repository.branch': branch,
|
||||
'general.pullRequest': pullRequest
|
||||
},
|
||||
{ ...configuration },
|
||||
{ upsert: true, new: true })
|
||||
} else {
|
||||
await Configuration.findOneAndUpdate({
|
||||
'repository.id': id,
|
||||
'repository.organization': organization,
|
||||
'repository.name': name,
|
||||
'repository.branch': branch,
|
||||
'general.pullRequest': { '$in': [null, 0] }
|
||||
},
|
||||
{ ...configuration },
|
||||
{ upsert: true, new: true })
|
||||
}
|
||||
|
||||
queueAndBuild(configuration, imageChanged);
|
||||
return {
|
||||
status: 201,
|
||||
@@ -103,11 +183,40 @@ export async function post(request: Request) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
// console.log(configuration)
|
||||
if (configuration) {
|
||||
cleanupTmp(configuration.general.workdir);
|
||||
await Deployment.findOneAndUpdate(
|
||||
{
|
||||
repoId: configuration.repository.id,
|
||||
branch: configuration.repository.branch,
|
||||
organization: configuration.repository.organization,
|
||||
name: configuration.repository.name,
|
||||
domain: configuration.publish.domain
|
||||
},
|
||||
{
|
||||
repoId: configuration.repository.id,
|
||||
branch: configuration.repository.branch,
|
||||
organization: configuration.repository.organization,
|
||||
name: configuration.repository.name,
|
||||
domain: configuration.publish.domain,
|
||||
progress: 'failed'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
error
|
||||
error: error.message || error
|
||||
}
|
||||
};
|
||||
} finally {
|
||||
try {
|
||||
await cleanupStuckedDeploymentsInDB();
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import Loading from '$components/Loading.svelte';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { page, session } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { browser } from '$app/env';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
|
||||
import Loading from '$components/Loading.svelte';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { session } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
@@ -84,13 +84,15 @@
|
||||
deployment.progress !== 'failed'}
|
||||
class:bg-warmGray-800={deployment.progress !== 'done' &&
|
||||
deployment.progress !== 'failed'}
|
||||
class:hover:bg-red-200={deployment.progress === 'failed'}
|
||||
class:hover:border-red-500={deployment.progress === 'failed'}
|
||||
on:click={() => goto(`./logs/${deployment.deployId}`)}
|
||||
>
|
||||
<div class="font-bold text-sm px-3 flex justify-center items-center">
|
||||
{deployment.branch}
|
||||
<div class="flex space-x-2 px-2">
|
||||
<div class="font-bold text-sm flex justify-center items-center">
|
||||
{deployment.branch}
|
||||
</div>
|
||||
<div class="font-bold text-xs flex justify-center items-center text-warmGray-500">{deployment.isPr ? 'PR' : ''}</div>
|
||||
</div>
|
||||
<div class="flex-1" />
|
||||
<div class="px-3 w-48">
|
||||
<div
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script>
|
||||
import { application, initialApplication, initConf, dashboard } from '$store';
|
||||
import { application, initialApplication, initConf, dashboard, prApplication } from '$store';
|
||||
import { onDestroy } from 'svelte';
|
||||
import Loading from '$components/Loading.svelte';
|
||||
import Navbar from '$components/Application/Navbar.svelte';
|
||||
import { page, session } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { browser } from '$app/env';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
|
||||
$application.repository.organization = $page.params.organization;
|
||||
$application.repository.name = $page.params.name;
|
||||
@@ -14,15 +14,16 @@
|
||||
|
||||
async function setConfiguration() {
|
||||
try {
|
||||
const config = await request(`/api/v1/application/config`, $session, {
|
||||
const { configuration } = await request(`/api/v1/application/config`, $session, {
|
||||
body: {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch
|
||||
}
|
||||
});
|
||||
$application = { ...config };
|
||||
$initConf = JSON.parse(JSON.stringify($application));
|
||||
$prApplication = configuration.filter((c) => c.general.pullRequest !== 0);
|
||||
$application = configuration.find((c) => c.general.pullRequest === 0);
|
||||
if (!$application) browser && goto('/dashboard/applications');
|
||||
} catch (error) {
|
||||
browser && goto('/dashboard/applications');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script context="module" lang="ts">
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
/**
|
||||
* @type {import('@sveltejs/kit').Load}
|
||||
*/
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -7,6 +7,8 @@
|
||||
import Mysql from '$components/Database/SVGs/Mysql.svelte';
|
||||
import { dashboard } from '$store';
|
||||
import { fade } from 'svelte/transition';
|
||||
import Redis from '$components/Database/SVGs/Redis.svelte';
|
||||
|
||||
</script>
|
||||
|
||||
<div class="py-5 text-left px-6 text-3xl tracking-tight font-bold flex items-center">
|
||||
@@ -56,6 +58,10 @@
|
||||
<CouchDb
|
||||
customClass="w-10 h-10 fill-current text-red-600 absolute top-0 left-0 -m-4"
|
||||
/>
|
||||
{:else if database.configuration.general.type == 'redis'}
|
||||
<Redis
|
||||
customClass="w-10 h-10 absolute top-0 left-0 -m-4"
|
||||
/>
|
||||
{:else if database.configuration.general.type == 'clickhouse'}
|
||||
<Clickhouse
|
||||
customClass="w-10 h-10 fill-current text-red-600 absolute top-0 left-0 -m-4"
|
||||
@@ -79,4 +85,3 @@
|
||||
<div class="text-2xl font-bold text-center">No databases found</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { database } from '$store';
|
||||
import { page, session } from '$app/stores';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { goto } from '$app/navigation';
|
||||
import MongoDb from '$components/Database/SVGs/MongoDb.svelte';
|
||||
@@ -12,10 +12,11 @@
|
||||
import PasswordField from '$components/PasswordField.svelte';
|
||||
import { browser } from '$app/env';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import Redis from '$components/Database/SVGs/Redis.svelte';
|
||||
|
||||
async function backup() {
|
||||
try {
|
||||
await request(`/api/v1/databases/${$page.params.name}/backup`, $session, {body: {}});
|
||||
await request(`/api/v1/databases/${$page.params.name}/backup`, $session, { body: {} });
|
||||
|
||||
browser && toast.push(`Successfully created backup.`);
|
||||
} catch (error) {
|
||||
@@ -56,6 +57,8 @@
|
||||
<Mysql customClass="w-8 h-8" />
|
||||
{:else if $database.config.general.type === 'couchdb'}
|
||||
<CouchDb customClass="w-8 h-8 fill-current text-red-600" />
|
||||
{:else if $database.config.general.type === 'redis'}
|
||||
<Redis customClass="w-8 h-8" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,6 +84,10 @@
|
||||
<PasswordField
|
||||
value={`http://${$database.envs.COUCHDB_USER}:${$database.envs.COUCHDB_PASSWORD}@${$database.config.general.deployId}:5984`}
|
||||
/>
|
||||
{:else if $database.config.general.type === 'redis'}
|
||||
<PasswordField
|
||||
value={`redis://${$database.envs.REDIS_PASSWORD}@${$database.config.general.deployId}:6379`}
|
||||
/>
|
||||
{:else if $database.config.general.type === 'clickhouse'}
|
||||
<!-- {JSON.stringify($database)} -->
|
||||
<!-- <textarea
|
||||
@@ -97,6 +104,12 @@
|
||||
<PasswordField value={$database.envs.MONGODB_ROOT_PASSWORD} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $database.config.general.type === 'redis'}
|
||||
<div class="flex items-center">
|
||||
<div class="font-bold w-64 text-warmGray-400">Redis password</div>
|
||||
<PasswordField value={$database.envs.REDIS_PASSWORD} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="pb-2 pt-5 space-y-4">
|
||||
<div class="text-2xl font-bold border-gradient w-32">Backup</div>
|
||||
<div class="pt-4">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { page, session } from '$app/stores';
|
||||
import Tooltip from '$components/Tooltip.svelte';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { database, initialDatabase } from '$store';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
import { session } from '$app/stores';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
|
||||
async function login() {
|
||||
const left = screen.width / 2 - 1020 / 2;
|
||||
@@ -20,7 +20,7 @@
|
||||
', toolbar=0, menubar=0, status=0'
|
||||
);
|
||||
const timer = setInterval(() => {
|
||||
if (newWindow.closed) {
|
||||
if (newWindow?.closed) {
|
||||
clearInterval(timer);
|
||||
browser && location.reload()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { page, session } from '$app/stores';
|
||||
import Tooltip from '$components/Tooltip.svelte';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
|
||||
import { page, session } from '$app/stores';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { goto } from '$app/navigation';
|
||||
import Loading from '$components/Loading.svelte';
|
||||
import Plausible from '$components/Service/Plausible.svelte';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { page, session } from '$app/stores';
|
||||
import Loading from '$components/Loading.svelte';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { initialNewService, newService } from '$store';
|
||||
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { newService } from '$store';
|
||||
import { page, session } from '$app/stores';
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { goto } from '$app/navigation';
|
||||
import Loading from '$components/Loading.svelte';
|
||||
import TooltipInfo from '$components/TooltipInfo.svelte';
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
<script>
|
||||
export let allowRegistration;
|
||||
export let sendErrors;
|
||||
|
||||
import { browser } from '$app/env';
|
||||
import { session } from '$app/stores';
|
||||
|
||||
import { request } from '$lib/api/request';
|
||||
import { request } from '$lib/request';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { fade } from 'svelte/transition';
|
||||
let settings = {
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
DateTimeFormatOptions,
|
||||
GithubInstallations
|
||||
} from 'src/global';
|
||||
import { writable, derived, readable, Writable } from 'svelte/store';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const dashboard = writable<Dashboard>({
|
||||
databases: {
|
||||
@@ -48,14 +48,21 @@ export const application = writable<Application>({
|
||||
general: {
|
||||
deployId: null,
|
||||
nickname: null,
|
||||
workdir: null
|
||||
workdir: null,
|
||||
isPreviewDeploymentEnabled: false,
|
||||
pullRequest: 0
|
||||
},
|
||||
build: {
|
||||
pack: 'static',
|
||||
directory: null,
|
||||
command: {
|
||||
build: null,
|
||||
installation: null
|
||||
installation: null,
|
||||
start: null,
|
||||
python: {
|
||||
module: null,
|
||||
instance: null
|
||||
}
|
||||
},
|
||||
container: {
|
||||
name: null,
|
||||
@@ -71,6 +78,7 @@ export const application = writable<Application>({
|
||||
secrets: []
|
||||
}
|
||||
});
|
||||
export const prApplication = writable([])
|
||||
|
||||
export const initConf = writable({});
|
||||
|
||||
@@ -92,14 +100,21 @@ export const initialApplication: Application = {
|
||||
general: {
|
||||
deployId: null,
|
||||
nickname: null,
|
||||
workdir: null
|
||||
workdir: null,
|
||||
isPreviewDeploymentEnabled: false,
|
||||
pullRequest: 0
|
||||
},
|
||||
build: {
|
||||
pack: 'static',
|
||||
directory: null,
|
||||
command: {
|
||||
build: null,
|
||||
installation: null
|
||||
installation: null,
|
||||
start: null,
|
||||
python: {
|
||||
module: null,
|
||||
instance: null
|
||||
}
|
||||
},
|
||||
container: {
|
||||
name: null,
|
||||
@@ -152,3 +167,5 @@ export const initialNewService = {
|
||||
userPasswordAgain: null,
|
||||
baseURL: null
|
||||
};
|
||||
|
||||
export const isPullRequestPermissionsGranted = writable(false)
|
||||
@@ -30,7 +30,7 @@ export default {
|
||||
alias: {
|
||||
$components: path.resolve('./src/components/'),
|
||||
$store: path.resolve('./src/store/index.ts'),
|
||||
$api: path.resolve('./src/routes/api/_index.ts'),
|
||||
$api: path.resolve('./src/routes/api/'),
|
||||
$models: path.resolve('./src/models/')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,30 +8,6 @@ const svelteClassColonExtractor = (content) => {
|
||||
module.exports = {
|
||||
mode: 'jit',
|
||||
purge: ['./**/*.html', './src/**/*.{js,jsx,ts,tsx,svelte}'],
|
||||
// purge: {
|
||||
// enabled: process.env.NODE_ENV === 'production',
|
||||
// content: ['./src/**/*.svelte', './src/**/*.html', './src/**/*.css', './index.html'],
|
||||
// preserveHtmlElements: true,
|
||||
// options: {
|
||||
// safelist: [
|
||||
// /svelte-/,
|
||||
// 'border-green-500',
|
||||
// 'border-yellow-300',
|
||||
// 'border-red-500',
|
||||
// 'hover:border-green-500',
|
||||
// 'hover:border-red-200',
|
||||
// 'hover:bg-red-200',
|
||||
// 'hover:bg-warmGray-900',
|
||||
// 'hover:bg-transparent'
|
||||
// ],
|
||||
// defaultExtractor: (content) => {
|
||||
// // WARNING: tailwindExtractor is internal tailwind api
|
||||
// // if this breaks after a tailwind update, report to svite repo
|
||||
// return [...tailwindExtractor(content), ...svelteClassColonExtractor(content)];
|
||||
// },
|
||||
// keyframes: false
|
||||
// }
|
||||
// },
|
||||
important: true,
|
||||
theme: {
|
||||
extend: {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"paths": {
|
||||
"$lib/*": ["src/lib/*"],
|
||||
"$store": ["src/store/index.ts"],
|
||||
"$api": ["src/routes/api/_index.ts"],
|
||||
"$api/*": ["src/routes/api/*"],
|
||||
"$models/*": ["src/models/*"],
|
||||
"$components/*": ["src/components/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user