Merge pull request #416 from coollabsio/next

v2.8.0
This commit is contained in:
Andras Bacsai 2022-05-10 11:50:07 +02:00 committed by GitHub
commit 79dfc6a660
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1310 additions and 622 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "coolify", "name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.", "description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "2.7.0", "version": "2.8.0",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0", "dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0",
@ -30,61 +30,62 @@
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-node": "1.0.0-next.73", "@sveltejs/adapter-node": "1.0.0-next.73",
"@sveltejs/kit": "1.0.0-next.316", "@sveltejs/kit": "1.0.0-next.326",
"@types/js-cookie": "3.0.1", "@types/js-cookie": "3.0.2",
"@types/js-yaml": "4.0.5", "@types/js-yaml": "4.0.5",
"@types/node": "17.0.25", "@types/node": "17.0.31",
"@types/node-forge": "1.0.1", "@types/node-forge": "1.0.2",
"@typescript-eslint/eslint-plugin": "4.31.1", "@typescript-eslint/eslint-plugin": "4.31.1",
"@typescript-eslint/parser": "4.31.1", "@typescript-eslint/parser": "4.31.1",
"@zerodevx/svelte-toast": "0.7.1", "@zerodevx/svelte-toast": "0.7.1",
"autoprefixer": "10.4.4", "autoprefixer": "10.4.7",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cross-var": "1.1.0", "cross-var": "1.1.0",
"eslint": "7.32.0", "eslint": "7.32.0",
"eslint-config-prettier": "8.5.0", "eslint-config-prettier": "8.5.0",
"eslint-plugin-svelte3": "3.4.1", "eslint-plugin-svelte3": "3.4.1",
"husky": "7.0.4", "husky": "7.0.4",
"lint-staged": "12.4.0", "lint-staged": "12.4.1",
"postcss": "8.4.12", "postcss": "8.4.13",
"prettier": "2.6.2", "prettier": "2.6.2",
"prettier-plugin-svelte": "2.7.0", "prettier-plugin-svelte": "2.7.0",
"prettier-plugin-tailwindcss": "0.1.10", "prettier-plugin-tailwindcss": "0.1.10",
"prisma": "3.11.1", "prisma": "3.11.1",
"svelte": "3.47.0", "svelte": "3.48.0",
"svelte-check": "2.7.0", "svelte-check": "2.7.0",
"svelte-preprocess": "4.10.6", "svelte-preprocess": "4.10.6",
"svelte-select": "4.4.7", "svelte-select": "4.4.7",
"sveltekit-i18n": "2.1.2", "sveltekit-i18n": "2.2.1",
"tailwindcss": "3.0.24", "tailwindcss": "3.0.24",
"ts-node": "10.7.0", "ts-node": "10.7.0",
"tslib": "2.3.1", "tslib": "2.4.0",
"typescript": "4.6.3" "typescript": "4.6.4"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@iarna/toml": "2.2.5", "@iarna/toml": "2.2.5",
"@prisma/client": "3.11.1", "@prisma/client": "3.11.1",
"@sentry/node": "6.19.6", "@sentry/node": "6.19.7",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"bullmq": "1.80.4", "bullmq": "1.81.4",
"compare-versions": "4.1.3", "compare-versions": "4.1.3",
"cookie": "0.5.0", "cookie": "0.5.0",
"cuid": "2.1.8", "cuid": "2.1.8",
"dayjs": "1.11.1", "dayjs": "1.11.2",
"dockerode": "3.3.1", "dockerode": "3.3.1",
"dotenv-extended": "2.9.0", "dotenv-extended": "2.9.0",
"generate-password": "1.7.0", "generate-password": "1.7.0",
"get-port": "6.1.2", "get-port": "6.1.2",
"got": "12.0.3", "got": "12.0.4",
"is-ip": "^4.0.0", "is-ip": "4.0.0",
"js-cookie": "3.0.1", "js-cookie": "3.0.1",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "8.5.1",
"mustache": "4.2.0", "mustache": "4.2.0",
"node-forge": "1.3.1", "node-forge": "1.3.1",
"node-os-utils": "1.3.6",
"p-limit": "4.0.0", "p-limit": "4.0.0",
"svelte-kit-cookie-session": "2.1.3", "svelte-kit-cookie-session": "2.1.4",
"tailwindcss-scrollbar": "0.1.0", "tailwindcss-scrollbar": "0.1.0",
"unique-names-generator": "4.7.1" "unique-names-generator": "4.7.1"
}, },

354
pnpm-lock.yaml generated
View File

@ -3,25 +3,25 @@ lockfileVersion: 5.4
specifiers: specifiers:
'@iarna/toml': 2.2.5 '@iarna/toml': 2.2.5
'@prisma/client': 3.11.1 '@prisma/client': 3.11.1
'@sentry/node': 6.19.6 '@sentry/node': 6.19.7
'@sveltejs/adapter-node': 1.0.0-next.73 '@sveltejs/adapter-node': 1.0.0-next.73
'@sveltejs/kit': 1.0.0-next.316 '@sveltejs/kit': 1.0.0-next.326
'@types/js-cookie': 3.0.1 '@types/js-cookie': 3.0.2
'@types/js-yaml': 4.0.5 '@types/js-yaml': 4.0.5
'@types/node': 17.0.25 '@types/node': 17.0.31
'@types/node-forge': 1.0.1 '@types/node-forge': 1.0.2
'@typescript-eslint/eslint-plugin': 4.31.1 '@typescript-eslint/eslint-plugin': 4.31.1
'@typescript-eslint/parser': 4.31.1 '@typescript-eslint/parser': 4.31.1
'@zerodevx/svelte-toast': 0.7.1 '@zerodevx/svelte-toast': 0.7.1
autoprefixer: 10.4.4 autoprefixer: 10.4.7
bcryptjs: 2.4.3 bcryptjs: 2.4.3
bullmq: 1.80.4 bullmq: 1.81.4
compare-versions: 4.1.3 compare-versions: 4.1.3
cookie: 0.5.0 cookie: 0.5.0
cross-env: 7.0.3 cross-env: 7.0.3
cross-var: 1.1.0 cross-var: 1.1.0
cuid: 2.1.8 cuid: 2.1.8
dayjs: 1.11.1 dayjs: 1.11.2
dockerode: 3.3.1 dockerode: 3.3.1
dotenv-extended: 2.9.0 dotenv-extended: 2.9.0
eslint: 7.32.0 eslint: 7.32.0
@ -29,92 +29,94 @@ specifiers:
eslint-plugin-svelte3: 3.4.1 eslint-plugin-svelte3: 3.4.1
generate-password: 1.7.0 generate-password: 1.7.0
get-port: 6.1.2 get-port: 6.1.2
got: 12.0.3 got: 12.0.4
husky: 7.0.4 husky: 7.0.4
is-ip: ^4.0.0 is-ip: 4.0.0
js-cookie: 3.0.1 js-cookie: 3.0.1
js-yaml: 4.1.0 js-yaml: 4.1.0
jsonwebtoken: 8.5.1 jsonwebtoken: 8.5.1
lint-staged: 12.4.0 lint-staged: 12.4.1
mustache: 4.2.0 mustache: 4.2.0
node-forge: 1.3.1 node-forge: 1.3.1
node-os-utils: 1.3.6
p-limit: 4.0.0 p-limit: 4.0.0
postcss: 8.4.12 postcss: 8.4.13
prettier: 2.6.2 prettier: 2.6.2
prettier-plugin-svelte: 2.7.0 prettier-plugin-svelte: 2.7.0
prettier-plugin-tailwindcss: 0.1.10 prettier-plugin-tailwindcss: 0.1.10
prisma: 3.11.1 prisma: 3.11.1
svelte: 3.47.0 svelte: 3.48.0
svelte-check: 2.7.0 svelte-check: 2.7.0
svelte-kit-cookie-session: 2.1.3 svelte-kit-cookie-session: 2.1.4
svelte-preprocess: 4.10.6 svelte-preprocess: 4.10.6
svelte-select: 4.4.7 svelte-select: 4.4.7
sveltekit-i18n: 2.1.2 sveltekit-i18n: 2.2.1
tailwindcss: 3.0.24 tailwindcss: 3.0.24
tailwindcss-scrollbar: 0.1.0 tailwindcss-scrollbar: 0.1.0
ts-node: 10.7.0 ts-node: 10.7.0
tslib: 2.3.1 tslib: 2.4.0
typescript: 4.6.3 typescript: 4.6.4
unique-names-generator: 4.7.1 unique-names-generator: 4.7.1
dependencies: dependencies:
'@iarna/toml': 2.2.5 '@iarna/toml': 2.2.5
'@prisma/client': 3.11.1_prisma@3.11.1 '@prisma/client': 3.11.1_prisma@3.11.1
'@sentry/node': 6.19.6 '@sentry/node': 6.19.7
bcryptjs: 2.4.3 bcryptjs: 2.4.3
bullmq: 1.80.4 bullmq: 1.81.4
compare-versions: 4.1.3 compare-versions: 4.1.3
cookie: 0.5.0 cookie: 0.5.0
cuid: 2.1.8 cuid: 2.1.8
dayjs: 1.11.1 dayjs: 1.11.2
dockerode: 3.3.1 dockerode: 3.3.1
dotenv-extended: 2.9.0 dotenv-extended: 2.9.0
generate-password: 1.7.0 generate-password: 1.7.0
get-port: 6.1.2 get-port: 6.1.2
got: 12.0.3 got: 12.0.4
is-ip: 4.0.0 is-ip: 4.0.0
js-cookie: 3.0.1 js-cookie: 3.0.1
js-yaml: 4.1.0 js-yaml: 4.1.0
jsonwebtoken: 8.5.1 jsonwebtoken: 8.5.1
mustache: 4.2.0 mustache: 4.2.0
node-forge: 1.3.1 node-forge: 1.3.1
node-os-utils: 1.3.6
p-limit: 4.0.0 p-limit: 4.0.0
svelte-kit-cookie-session: 2.1.3 svelte-kit-cookie-session: 2.1.4
tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.24 tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.24
unique-names-generator: 4.7.1 unique-names-generator: 4.7.1
devDependencies: devDependencies:
'@sveltejs/adapter-node': 1.0.0-next.73 '@sveltejs/adapter-node': 1.0.0-next.73
'@sveltejs/kit': 1.0.0-next.316_svelte@3.47.0 '@sveltejs/kit': 1.0.0-next.326_svelte@3.48.0
'@types/js-cookie': 3.0.1 '@types/js-cookie': 3.0.2
'@types/js-yaml': 4.0.5 '@types/js-yaml': 4.0.5
'@types/node': 17.0.25 '@types/node': 17.0.31
'@types/node-forge': 1.0.1 '@types/node-forge': 1.0.2
'@typescript-eslint/eslint-plugin': 4.31.1_r3ph5xlwsrsg4ewthrjemd3cfq '@typescript-eslint/eslint-plugin': 4.31.1_lii63oz3usekbu5ehvrcuwn5jy
'@typescript-eslint/parser': 4.31.1_hrkuebk64jiu2ut2d2sm4oylnu '@typescript-eslint/parser': 4.31.1_e4zyhrvfnqudwdx5bevnvkluy4
'@zerodevx/svelte-toast': 0.7.1 '@zerodevx/svelte-toast': 0.7.1
autoprefixer: 10.4.4_postcss@8.4.12 autoprefixer: 10.4.7_postcss@8.4.13
cross-env: 7.0.3 cross-env: 7.0.3
cross-var: 1.1.0 cross-var: 1.1.0
eslint: 7.32.0 eslint: 7.32.0
eslint-config-prettier: 8.5.0_eslint@7.32.0 eslint-config-prettier: 8.5.0_eslint@7.32.0
eslint-plugin-svelte3: 3.4.1_4oxeyilw5mxcaksmcxtpjddhfe eslint-plugin-svelte3: 3.4.1_6wevxxng4y4ff26nzlndg2wnpa
husky: 7.0.4 husky: 7.0.4
lint-staged: 12.4.0 lint-staged: 12.4.1
postcss: 8.4.12 postcss: 8.4.13
prettier: 2.6.2 prettier: 2.6.2
prettier-plugin-svelte: 2.7.0_sqtt6dzjlskmywoml5ykunxlce prettier-plugin-svelte: 2.7.0_kkjbqzpydplecjtkxrgomroeru
prettier-plugin-tailwindcss: 0.1.10_prettier@2.6.2 prettier-plugin-tailwindcss: 0.1.10_prettier@2.6.2
prisma: 3.11.1 prisma: 3.11.1
svelte: 3.47.0 svelte: 3.48.0
svelte-check: 2.7.0_cp6olp7pwsfaq5mjijwt65d6uy svelte-check: 2.7.0_f2ke6qjyzu5axsjd6yk3u4tn7a
svelte-preprocess: 4.10.6_igaqrb5onrwvsmrrc32h4m72ha svelte-preprocess: 4.10.6_nq4dx2skq5drra53vttuo4lltu
svelte-select: 4.4.7 svelte-select: 4.4.7
sveltekit-i18n: 2.1.2_svelte@3.47.0 sveltekit-i18n: 2.2.1_svelte@3.48.0
tailwindcss: 3.0.24_ts-node@10.7.0 tailwindcss: 3.0.24_ts-node@10.7.0
ts-node: 10.7.0_3z6inmgn4ud4moqealnfxgbl2m ts-node: 10.7.0_l47be6km5p57gglrggidw5gsgm
tslib: 2.3.1 tslib: 2.4.0
typescript: 4.6.3 typescript: 4.6.4
packages: packages:
/@babel/code-frame/7.12.11: /@babel/code-frame/7.12.11:
@ -285,55 +287,55 @@ packages:
picomatch: 2.3.0 picomatch: 2.3.0
dev: true dev: true
/@sentry/core/6.19.6: /@sentry/core/6.19.7:
resolution: resolution:
{ {
integrity: sha512-biEotGRr44/vBCOegkTfC9rwqaqRKIpFljKGyYU6/NtzMRooktqOhjmjmItNCMRknArdeaQwA8lk2jcZDXX3Og== integrity: sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==
} }
engines: { node: '>=6' } engines: { node: '>=6' }
dependencies: dependencies:
'@sentry/hub': 6.19.6 '@sentry/hub': 6.19.7
'@sentry/minimal': 6.19.6 '@sentry/minimal': 6.19.7
'@sentry/types': 6.19.6 '@sentry/types': 6.19.7
'@sentry/utils': 6.19.6 '@sentry/utils': 6.19.7
tslib: 1.14.1 tslib: 1.14.1
dev: false dev: false
/@sentry/hub/6.19.6: /@sentry/hub/6.19.7:
resolution: resolution:
{ {
integrity: sha512-PuEOBZxvx3bjxcXmWWZfWXG+orojQiWzv9LQXjIgroVMKM/GG4QtZbnWl1hOckUj7WtKNl4hEGO2g/6PyCV/vA== integrity: sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==
} }
engines: { node: '>=6' } engines: { node: '>=6' }
dependencies: dependencies:
'@sentry/types': 6.19.6 '@sentry/types': 6.19.7
'@sentry/utils': 6.19.6 '@sentry/utils': 6.19.7
tslib: 1.14.1 tslib: 1.14.1
dev: false dev: false
/@sentry/minimal/6.19.6: /@sentry/minimal/6.19.7:
resolution: resolution:
{ {
integrity: sha512-T1NKcv+HTlmd8EbzUgnGPl4ySQGHWMCyZ8a8kXVMZOPDzphN3fVIzkYzWmSftCWp0rpabXPt9aRF2mfBKU+mAQ== integrity: sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==
} }
engines: { node: '>=6' } engines: { node: '>=6' }
dependencies: dependencies:
'@sentry/hub': 6.19.6 '@sentry/hub': 6.19.7
'@sentry/types': 6.19.6 '@sentry/types': 6.19.7
tslib: 1.14.1 tslib: 1.14.1
dev: false dev: false
/@sentry/node/6.19.6: /@sentry/node/6.19.7:
resolution: resolution:
{ {
integrity: sha512-kHQMfsy40ZxxdS9zMPmXCOOLWOJbQj6/aVSHt/L1QthYcgkAi7NJQNXnQIPWQDe8eP3DfNIWM7dc446coqjXrQ== integrity: sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg==
} }
engines: { node: '>=6' } engines: { node: '>=6' }
dependencies: dependencies:
'@sentry/core': 6.19.6 '@sentry/core': 6.19.7
'@sentry/hub': 6.19.6 '@sentry/hub': 6.19.7
'@sentry/types': 6.19.6 '@sentry/types': 6.19.7
'@sentry/utils': 6.19.6 '@sentry/utils': 6.19.7
cookie: 0.4.2 cookie: 0.4.2
https-proxy-agent: 5.0.0 https-proxy-agent: 5.0.0
lru_map: 0.3.3 lru_map: 0.3.3
@ -342,22 +344,22 @@ packages:
- supports-color - supports-color
dev: false dev: false
/@sentry/types/6.19.6: /@sentry/types/6.19.7:
resolution: resolution:
{ {
integrity: sha512-QH34LMJidEUPZK78l+Frt3AaVFJhEmIi05Zf8WHd9/iTt+OqvCHBgq49DDr1FWFqyYWm/QgW/3bIoikFpfsXyQ== integrity: sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==
} }
engines: { node: '>=6' } engines: { node: '>=6' }
dev: false dev: false
/@sentry/utils/6.19.6: /@sentry/utils/6.19.7:
resolution: resolution:
{ {
integrity: sha512-fAMWcsguL0632eWrROp/vhPgI7sBj/JROWVPzpabwVkm9z3m1rQm6iLFn4qfkZL8Ozy6NVZPXOQ7EXmeU24byg== integrity: sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==
} }
engines: { node: '>=6' } engines: { node: '>=6' }
dependencies: dependencies:
'@sentry/types': 6.19.6 '@sentry/types': 6.19.7
tslib: 1.14.1 tslib: 1.14.1
dev: false dev: false
@ -378,19 +380,20 @@ packages:
tiny-glob: 0.2.9 tiny-glob: 0.2.9
dev: true dev: true
/@sveltejs/kit/1.0.0-next.316_svelte@3.47.0: /@sveltejs/kit/1.0.0-next.326_svelte@3.48.0:
resolution: resolution:
{ {
integrity: sha512-oLjWOWzjriJD2t210r7ALuH/8ZADrJGsOODzRCRSJvRBCt0Q7VKVLqwKbM/RlZzD1k8Af2uRodQT11kP98hAIA== integrity: sha512-prJqmXZ2H1wmFfnMw7wDujfbkcA8vuubuqUkpVVmXhfh2+SEzQscPTNwxoE5EJxb5sywtLWEvYx3hv1gPS4Lvg==
} }
engines: { node: '>=14.13' } engines: { node: '>=14.13' }
hasBin: true hasBin: true
peerDependencies: peerDependencies:
svelte: ^3.44.0 svelte: ^3.44.0
dependencies: dependencies:
'@sveltejs/vite-plugin-svelte': 1.0.0-next.33_svelte@3.47.0+vite@2.9.1 '@sveltejs/vite-plugin-svelte': 1.0.0-next.33_svelte@3.48.0+vite@2.9.1
chokidar: 3.5.3
sade: 1.7.4 sade: 1.7.4
svelte: 3.47.0 svelte: 3.48.0
vite: 2.9.1 vite: 2.9.1
transitivePeerDependencies: transitivePeerDependencies:
- diff-match-patch - diff-match-patch
@ -400,7 +403,7 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@sveltejs/vite-plugin-svelte/1.0.0-next.33_svelte@3.47.0+vite@2.9.1: /@sveltejs/vite-plugin-svelte/1.0.0-next.33_svelte@3.48.0+vite@2.9.1:
resolution: resolution:
{ {
integrity: sha512-aj0h2+ZixgT+yoJFIs8dRRw/Cj9tgNu3+hY4CJikpa04mfhR61wXqJFfi2ZEFMUvFda5nCxKYIChFkc6wq5fJA== integrity: sha512-aj0h2+ZixgT+yoJFIs8dRRw/Cj9tgNu3+hY4CJikpa04mfhR61wXqJFfi2ZEFMUvFda5nCxKYIChFkc6wq5fJA==
@ -419,22 +422,22 @@ packages:
kleur: 4.1.4 kleur: 4.1.4
magic-string: 0.25.7 magic-string: 0.25.7
require-relative: 0.8.7 require-relative: 0.8.7
svelte: 3.47.0 svelte: 3.48.0
svelte-hmr: 0.14.9_svelte@3.47.0 svelte-hmr: 0.14.9_svelte@3.48.0
vite: 2.9.1 vite: 2.9.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
/@sveltekit-i18n/base/1.1.1_svelte@3.47.0: /@sveltekit-i18n/base/1.2.1_svelte@3.48.0:
resolution: resolution:
{ {
integrity: sha512-J/sMU0OwS3dCLOuilHMBqu8vZHuuXiNV9vFJx8Nb4/b5BlR/KCZ4bCXI8wZR02GHeCOYKZxWus07CM1scxa/jw== integrity: sha512-F8gqG2+KAOeT0o2wYlUrW3TRCX7zaD7rBy/1CEVNw0irfw9TgFf/ODmhubkHHT3+6Zk+SMz8RNgeuffBfAMbJw==
} }
peerDependencies: peerDependencies:
svelte: ^3.x svelte: ^3.x
dependencies: dependencies:
svelte: 3.47.0 svelte: 3.48.0
optionalDependencies: optionalDependencies:
'@sveltekit-i18n/parser-default': 1.0.3 '@sveltekit-i18n/parser-default': 1.0.3
dev: true dev: true
@ -492,7 +495,7 @@ packages:
dependencies: dependencies:
'@types/http-cache-semantics': 4.0.1 '@types/http-cache-semantics': 4.0.1
'@types/keyv': 3.1.3 '@types/keyv': 3.1.3
'@types/node': 17.0.25 '@types/node': 17.0.31
'@types/responselike': 1.0.0 '@types/responselike': 1.0.0
dev: false dev: false
@ -503,10 +506,10 @@ packages:
} }
dev: false dev: false
/@types/js-cookie/3.0.1: /@types/js-cookie/3.0.2:
resolution: resolution:
{ {
integrity: sha512-7wg/8gfHltklehP+oyJnZrz9XBuX5ZPP4zB6UsI84utdlkRYLnOm2HfpLXazTwZA+fpGn0ir8tGNgVnMEleBGQ== integrity: sha512-6+0ekgfusHftJNYpihfkMu8BWdeHs9EOJuGcSofErjstGPfPGEu9yTu4t460lTzzAMl2cM5zngQJqPMHbbnvYA==
} }
dev: true dev: true
@ -530,22 +533,22 @@ packages:
integrity: sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== integrity: sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==
} }
dependencies: dependencies:
'@types/node': 17.0.25 '@types/node': 17.0.31
dev: false dev: false
/@types/node-forge/1.0.1: /@types/node-forge/1.0.2:
resolution: resolution:
{ {
integrity: sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA== integrity: sha512-J1OkeZGaW0y9Y7xD49Ja8O82B9l5nZDeoYuGWqIOYPAf9LR+xF23k9ILdzv8dH+2H033fx3D5oiA0GlmicI+sg==
} }
dependencies: dependencies:
'@types/node': 17.0.25 '@types/node': 17.0.31
dev: true dev: true
/@types/node/17.0.25: /@types/node/17.0.31:
resolution: resolution:
{ {
integrity: sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w== integrity: sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==
} }
/@types/pug/2.0.5: /@types/pug/2.0.5:
@ -561,7 +564,7 @@ packages:
integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
} }
dependencies: dependencies:
'@types/node': 17.0.25 '@types/node': 17.0.31
dev: false dev: false
/@types/sass/1.16.1: /@types/sass/1.16.1:
@ -570,10 +573,10 @@ packages:
integrity: sha512-iZUcRrGuz/Tbg3loODpW7vrQJkUtpY2fFSf4ELqqkApcS2TkZ1msk7ie8iZPB86lDOP8QOTTmuvWjc5S0R9OjQ== integrity: sha512-iZUcRrGuz/Tbg3loODpW7vrQJkUtpY2fFSf4ELqqkApcS2TkZ1msk7ie8iZPB86lDOP8QOTTmuvWjc5S0R9OjQ==
} }
dependencies: dependencies:
'@types/node': 17.0.25 '@types/node': 17.0.31
dev: true dev: true
/@typescript-eslint/eslint-plugin/4.31.1_r3ph5xlwsrsg4ewthrjemd3cfq: /@typescript-eslint/eslint-plugin/4.31.1_lii63oz3usekbu5ehvrcuwn5jy:
resolution: resolution:
{ {
integrity: sha512-UDqhWmd5i0TvPLmbK5xY3UZB0zEGseF+DHPghZ37Sb83Qd3p8ujhvAtkU4OF46Ka5Pm5kWvFIx0cCTBFKo0alA== integrity: sha512-UDqhWmd5i0TvPLmbK5xY3UZB0zEGseF+DHPghZ37Sb83Qd3p8ujhvAtkU4OF46Ka5Pm5kWvFIx0cCTBFKo0alA==
@ -587,21 +590,21 @@ packages:
typescript: typescript:
optional: true optional: true
dependencies: dependencies:
'@typescript-eslint/experimental-utils': 4.31.1_hrkuebk64jiu2ut2d2sm4oylnu '@typescript-eslint/experimental-utils': 4.31.1_e4zyhrvfnqudwdx5bevnvkluy4
'@typescript-eslint/parser': 4.31.1_hrkuebk64jiu2ut2d2sm4oylnu '@typescript-eslint/parser': 4.31.1_e4zyhrvfnqudwdx5bevnvkluy4
'@typescript-eslint/scope-manager': 4.31.1 '@typescript-eslint/scope-manager': 4.31.1
debug: 4.3.3 debug: 4.3.3
eslint: 7.32.0 eslint: 7.32.0
functional-red-black-tree: 1.0.1 functional-red-black-tree: 1.0.1
regexpp: 3.2.0 regexpp: 3.2.0
semver: 7.3.5 semver: 7.3.5
tsutils: 3.21.0_typescript@4.6.3 tsutils: 3.21.0_typescript@4.6.4
typescript: 4.6.3 typescript: 4.6.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
/@typescript-eslint/experimental-utils/4.31.1_hrkuebk64jiu2ut2d2sm4oylnu: /@typescript-eslint/experimental-utils/4.31.1_e4zyhrvfnqudwdx5bevnvkluy4:
resolution: resolution:
{ {
integrity: sha512-NtoPsqmcSsWty0mcL5nTZXMf7Ei0Xr2MT8jWjXMVgRK0/1qeQ2jZzLFUh4QtyJ4+/lPUyMw5cSfeeME+Zrtp9Q== integrity: sha512-NtoPsqmcSsWty0mcL5nTZXMf7Ei0Xr2MT8jWjXMVgRK0/1qeQ2jZzLFUh4QtyJ4+/lPUyMw5cSfeeME+Zrtp9Q==
@ -613,7 +616,7 @@ packages:
'@types/json-schema': 7.0.9 '@types/json-schema': 7.0.9
'@typescript-eslint/scope-manager': 4.31.1 '@typescript-eslint/scope-manager': 4.31.1
'@typescript-eslint/types': 4.31.1 '@typescript-eslint/types': 4.31.1
'@typescript-eslint/typescript-estree': 4.31.1_typescript@4.6.3 '@typescript-eslint/typescript-estree': 4.31.1_typescript@4.6.4
eslint: 7.32.0 eslint: 7.32.0
eslint-scope: 5.1.1 eslint-scope: 5.1.1
eslint-utils: 3.0.0_eslint@7.32.0 eslint-utils: 3.0.0_eslint@7.32.0
@ -622,7 +625,7 @@ packages:
- typescript - typescript
dev: true dev: true
/@typescript-eslint/parser/4.31.1_hrkuebk64jiu2ut2d2sm4oylnu: /@typescript-eslint/parser/4.31.1_e4zyhrvfnqudwdx5bevnvkluy4:
resolution: resolution:
{ {
integrity: sha512-dnVZDB6FhpIby6yVbHkwTKkn2ypjVIfAR9nh+kYsA/ZL0JlTsd22BiDjouotisY3Irmd3OW1qlk9EI5R8GrvRQ== integrity: sha512-dnVZDB6FhpIby6yVbHkwTKkn2ypjVIfAR9nh+kYsA/ZL0JlTsd22BiDjouotisY3Irmd3OW1qlk9EI5R8GrvRQ==
@ -637,10 +640,10 @@ packages:
dependencies: dependencies:
'@typescript-eslint/scope-manager': 4.31.1 '@typescript-eslint/scope-manager': 4.31.1
'@typescript-eslint/types': 4.31.1 '@typescript-eslint/types': 4.31.1
'@typescript-eslint/typescript-estree': 4.31.1_typescript@4.6.3 '@typescript-eslint/typescript-estree': 4.31.1_typescript@4.6.4
debug: 4.3.3 debug: 4.3.3
eslint: 7.32.0 eslint: 7.32.0
typescript: 4.6.3 typescript: 4.6.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@ -664,7 +667,7 @@ packages:
engines: { node: ^8.10.0 || ^10.13.0 || >=11.10.1 } engines: { node: ^8.10.0 || ^10.13.0 || >=11.10.1 }
dev: true dev: true
/@typescript-eslint/typescript-estree/4.31.1_typescript@4.6.3: /@typescript-eslint/typescript-estree/4.31.1_typescript@4.6.4:
resolution: resolution:
{ {
integrity: sha512-EGHkbsUvjFrvRnusk6yFGqrqMBTue5E5ROnS5puj3laGQPasVUgwhrxfcgkdHNFECHAewpvELE1Gjv0XO3mdWg== integrity: sha512-EGHkbsUvjFrvRnusk6yFGqrqMBTue5E5ROnS5puj3laGQPasVUgwhrxfcgkdHNFECHAewpvELE1Gjv0XO3mdWg==
@ -682,8 +685,8 @@ packages:
globby: 11.0.4 globby: 11.0.4
is-glob: 4.0.3 is-glob: 4.0.3
semver: 7.3.5 semver: 7.3.5
tsutils: 3.21.0_typescript@4.6.3 tsutils: 3.21.0_typescript@4.6.4
typescript: 4.6.3 typescript: 4.6.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@ -956,22 +959,22 @@ packages:
typpy: 2.3.11 typpy: 2.3.11
dev: false dev: false
/autoprefixer/10.4.4_postcss@8.4.12: /autoprefixer/10.4.7_postcss@8.4.13:
resolution: resolution:
{ {
integrity: sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA== integrity: sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==
} }
engines: { node: ^10 || ^12 || >=14 } engines: { node: ^10 || ^12 || >=14 }
hasBin: true hasBin: true
peerDependencies: peerDependencies:
postcss: ^8.1.0 postcss: ^8.1.0
dependencies: dependencies:
browserslist: 4.20.2 browserslist: 4.20.3
caniuse-lite: 1.0.30001320 caniuse-lite: 1.0.30001338
fraction.js: 4.2.0 fraction.js: 4.2.0
normalize-range: 0.1.2 normalize-range: 0.1.2
picocolors: 1.0.0 picocolors: 1.0.0
postcss: 8.4.12 postcss: 8.4.13
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
dev: true dev: true
@ -1653,18 +1656,18 @@ packages:
fill-range: 7.0.1 fill-range: 7.0.1
dev: true dev: true
/browserslist/4.20.2: /browserslist/4.20.3:
resolution: resolution:
{ {
integrity: sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA== integrity: sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==
} }
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
hasBin: true hasBin: true
dependencies: dependencies:
caniuse-lite: 1.0.30001320 caniuse-lite: 1.0.30001338
electron-to-chromium: 1.4.93 electron-to-chromium: 1.4.137
escalade: 3.1.1 escalade: 3.1.1
node-releases: 2.0.2 node-releases: 2.0.4
picocolors: 1.0.0 picocolors: 1.0.0
dev: true dev: true
@ -1686,10 +1689,10 @@ packages:
ieee754: 1.2.1 ieee754: 1.2.1
dev: false dev: false
/bullmq/1.80.4: /bullmq/1.81.4:
resolution: resolution:
{ {
integrity: sha512-j3PyjU16gqmb3Md9QjMInJdbMvxIlSJx7mojtoP06LV9MfhzW75DkDrpSuJlF0H+0+u6MViV4hhaGTxky5OJWw== integrity: sha512-sUEWOMKZnWlh1/XNqYAoSwXW6P8nZN7uJiHKZ8XlZCiIxWlEGjFtlugkkiCZ0lsTI2nNRHdxfpn78x9K3L1utQ==
} }
dependencies: dependencies:
cron-parser: 4.2.1 cron-parser: 4.2.1
@ -1753,10 +1756,10 @@ packages:
engines: { node: '>=6' } engines: { node: '>=6' }
dev: false dev: false
/caniuse-lite/1.0.30001320: /caniuse-lite/1.0.30001338:
resolution: resolution:
{ {
integrity: sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA== integrity: sha512-1gLHWyfVoRDsHieO+CaeYe7jSo/MT7D7lhaXUiwwbuR5BwQxORs0f1tAwUSQr3YbxRXJvxHM/PA5FfPQRnsPeQ==
} }
dev: true dev: true
@ -2048,10 +2051,10 @@ packages:
} }
dev: false dev: false
/dayjs/1.11.1: /dayjs/1.11.2:
resolution: resolution:
{ {
integrity: sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA== integrity: sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==
} }
dev: false dev: false
@ -2267,10 +2270,10 @@ packages:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
dev: false dev: false
/electron-to-chromium/1.4.93: /electron-to-chromium/1.4.137:
resolution: resolution:
{ {
integrity: sha512-ywq9Pc5Gwwpv7NG767CtoU8xF3aAUQJjH9//Wy3MBCg4w5JSLbJUq2L8IsCdzPMjvSgxuue9WcVaTOyyxCL0aQ== integrity: sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==
} }
dev: true dev: true
@ -2615,7 +2618,7 @@ packages:
eslint: 7.32.0 eslint: 7.32.0
dev: true dev: true
/eslint-plugin-svelte3/3.4.1_4oxeyilw5mxcaksmcxtpjddhfe: /eslint-plugin-svelte3/3.4.1_6wevxxng4y4ff26nzlndg2wnpa:
resolution: resolution:
{ {
integrity: sha512-7p59WG8qV8L6wLdl4d/c3mdjkgVglQCdv5XOTk/iNPBKXuuV+Q0eFP5Wa6iJd/G2M1qR3BkLPEzaANOqKAZczw== integrity: sha512-7p59WG8qV8L6wLdl4d/c3mdjkgVglQCdv5XOTk/iNPBKXuuV+Q0eFP5Wa6iJd/G2M1qR3BkLPEzaANOqKAZczw==
@ -2626,7 +2629,7 @@ packages:
svelte: ^3.2.0 svelte: ^3.2.0
dependencies: dependencies:
eslint: 7.32.0 eslint: 7.32.0
svelte: 3.47.0 svelte: 3.48.0
dev: true dev: true
/eslint-scope/5.1.1: /eslint-scope/5.1.1:
@ -3080,10 +3083,10 @@ packages:
} }
dev: true dev: true
/got/12.0.3: /got/12.0.4:
resolution: resolution:
{ {
integrity: sha512-hmdcXi/S0gcAtDg4P8j/rM7+j3o1Aq6bXhjxkDhRY2ipe7PHpvx/14DgTY2czHOLaGeU8VRvRecidwfu9qdFug== integrity: sha512-2Eyz4iU/ktq7wtMFXxzK7g5p35uNYLLdiZarZ5/Yn3IJlNEpBd5+dCgcAyxN8/8guZLszffwe3wVyw+DEVrpBg==
} }
engines: { node: '>=14.16' } engines: { node: '>=14.16' }
dependencies: dependencies:
@ -3544,10 +3547,10 @@ packages:
engines: { node: '>=10' } engines: { node: '>=10' }
dev: true dev: true
/lint-staged/12.4.0: /lint-staged/12.4.1:
resolution: resolution:
{ {
integrity: sha512-3X7MR0h9b7qf4iXf/1n7RlVAx+EzpAZXoCEMhVSpaBlgKDfH2ewf+QUm7BddFyq29v4dgPP+8+uYpWuSWx035A== integrity: sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg==
} }
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
hasBin: true hasBin: true
@ -3884,10 +3887,10 @@ packages:
dev: false dev: false
optional: true optional: true
/nanoid/3.3.1: /nanoid/3.3.4:
resolution: resolution:
{ {
integrity: sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
} }
engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 }
hasBin: true hasBin: true
@ -3914,10 +3917,17 @@ packages:
dev: false dev: false
optional: true optional: true
/node-releases/2.0.2: /node-os-utils/1.3.6:
resolution: resolution:
{ {
integrity: sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== integrity: sha512-WympE9ELtdOzNak/rAuuIV5DwvX/PTJtN0LjyWeGyTTR2Kt0sY56ldLoGbVBnfM1dz46VeO3sHcNZI5BZ+EB+w==
}
dev: false
/node-releases/2.0.4:
resolution:
{
integrity: sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==
} }
dev: true dev: true
@ -4110,7 +4120,7 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/postcss-js/4.0.0_postcss@8.4.12: /postcss-js/4.0.0_postcss@8.4.13:
resolution: resolution:
{ {
integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ== integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==
@ -4120,10 +4130,10 @@ packages:
postcss: ^8.3.3 postcss: ^8.3.3
dependencies: dependencies:
camelcase-css: 2.0.1 camelcase-css: 2.0.1
postcss: 8.4.12 postcss: 8.4.13
dev: true dev: true
/postcss-load-config/3.1.4_ysmyu6g5dtd6yanj6zrab4uqoy: /postcss-load-config/3.1.4_4jqnslpwnj4ifyjfqbkuebd4fy:
resolution: resolution:
{ {
integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
@ -4139,12 +4149,12 @@ packages:
optional: true optional: true
dependencies: dependencies:
lilconfig: 2.0.5 lilconfig: 2.0.5
postcss: 8.4.12 postcss: 8.4.13
ts-node: 10.7.0_3z6inmgn4ud4moqealnfxgbl2m ts-node: 10.7.0_l47be6km5p57gglrggidw5gsgm
yaml: 1.10.2 yaml: 1.10.2
dev: true dev: true
/postcss-nested/5.0.6_postcss@8.4.12: /postcss-nested/5.0.6_postcss@8.4.13:
resolution: resolution:
{ {
integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA== integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==
@ -4153,7 +4163,7 @@ packages:
peerDependencies: peerDependencies:
postcss: ^8.2.14 postcss: ^8.2.14
dependencies: dependencies:
postcss: 8.4.12 postcss: 8.4.13
postcss-selector-parser: 6.0.10 postcss-selector-parser: 6.0.10
dev: true dev: true
@ -4175,14 +4185,14 @@ packages:
} }
dev: true dev: true
/postcss/8.4.12: /postcss/8.4.13:
resolution: resolution:
{ {
integrity: sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== integrity: sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==
} }
engines: { node: ^10 || ^12 || >=14 } engines: { node: ^10 || ^12 || >=14 }
dependencies: dependencies:
nanoid: 3.3.1 nanoid: 3.3.4
picocolors: 1.0.0 picocolors: 1.0.0
source-map-js: 1.0.2 source-map-js: 1.0.2
dev: true dev: true
@ -4195,7 +4205,7 @@ packages:
engines: { node: '>= 0.8.0' } engines: { node: '>= 0.8.0' }
dev: true dev: true
/prettier-plugin-svelte/2.7.0_sqtt6dzjlskmywoml5ykunxlce: /prettier-plugin-svelte/2.7.0_kkjbqzpydplecjtkxrgomroeru:
resolution: resolution:
{ {
integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA== integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==
@ -4205,7 +4215,7 @@ packages:
svelte: ^3.2.0 svelte: ^3.2.0
dependencies: dependencies:
prettier: 2.6.2 prettier: 2.6.2
svelte: 3.47.0 svelte: 3.48.0
dev: true dev: true
/prettier-plugin-tailwindcss/0.1.10_prettier@2.6.2: /prettier-plugin-tailwindcss/0.1.10_prettier@2.6.2:
@ -4513,7 +4523,7 @@ packages:
integrity: sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w== integrity: sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==
} }
dependencies: dependencies:
tslib: 2.3.1 tslib: 2.4.0
dev: true dev: true
/sade/1.7.4: /sade/1.7.4:
@ -4878,7 +4888,7 @@ packages:
engines: { node: '>= 0.4' } engines: { node: '>= 0.4' }
dev: true dev: true
/svelte-check/2.7.0_cp6olp7pwsfaq5mjijwt65d6uy: /svelte-check/2.7.0_f2ke6qjyzu5axsjd6yk3u4tn7a:
resolution: resolution:
{ {
integrity: sha512-GrvG24j0+i8AOm0k0KyJ6Dqc+TAR2yzB7rtS4nljHStunVxCTr/1KYlv4EsOeoqtHLzeWMOd5D2O6nDdP/yw4A== integrity: sha512-GrvG24j0+i8AOm0k0KyJ6Dqc+TAR2yzB7rtS4nljHStunVxCTr/1KYlv4EsOeoqtHLzeWMOd5D2O6nDdP/yw4A==
@ -4893,9 +4903,9 @@ packages:
picocolors: 1.0.0 picocolors: 1.0.0
sade: 1.7.4 sade: 1.7.4
source-map: 0.7.3 source-map: 0.7.3
svelte: 3.47.0 svelte: 3.48.0
svelte-preprocess: 4.10.6_igaqrb5onrwvsmrrc32h4m72ha svelte-preprocess: 4.10.6_nq4dx2skq5drra53vttuo4lltu
typescript: 4.6.3 typescript: 4.6.4
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
- coffeescript - coffeescript
@ -4909,7 +4919,7 @@ packages:
- sugarss - sugarss
dev: true dev: true
/svelte-hmr/0.14.9_svelte@3.47.0: /svelte-hmr/0.14.9_svelte@3.48.0:
resolution: resolution:
{ {
integrity: sha512-bKE9+4qb4sAnA+TKHiYurUl970rjA0XmlP9TEP7K/ncyWz3m81kA4HOgmlZK/7irGK7gzZlaPDI3cmf8fp/+tg== integrity: sha512-bKE9+4qb4sAnA+TKHiYurUl970rjA0XmlP9TEP7K/ncyWz3m81kA4HOgmlZK/7irGK7gzZlaPDI3cmf8fp/+tg==
@ -4917,17 +4927,17 @@ packages:
peerDependencies: peerDependencies:
svelte: '>=3.19.0' svelte: '>=3.19.0'
dependencies: dependencies:
svelte: 3.47.0 svelte: 3.48.0
dev: true dev: true
/svelte-kit-cookie-session/2.1.3: /svelte-kit-cookie-session/2.1.4:
resolution: resolution:
{ {
integrity: sha512-7Xk3CNbpLAi1KodlsV5W5jULQ2NxQunaXtAYqAuzIEXIq2EwC4oDa25kdmHjNe33epV0t4r0WwxZOuSdJPsapg== integrity: sha512-z/ckxHWguYyy66UqfId4Lu+A77ft/3mV5oozbRTI9bnQY0tyJfns/SM0ikdkj7OV5GiI8kq7GSOGvajWwyGeZw==
} }
dev: false dev: false
/svelte-preprocess/4.10.6_igaqrb5onrwvsmrrc32h4m72ha: /svelte-preprocess/4.10.6_nq4dx2skq5drra53vttuo4lltu:
resolution: resolution:
{ {
integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ== integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==
@ -4975,11 +4985,11 @@ packages:
'@types/sass': 1.16.1 '@types/sass': 1.16.1
detect-indent: 6.1.0 detect-indent: 6.1.0
magic-string: 0.25.7 magic-string: 0.25.7
postcss: 8.4.12 postcss: 8.4.13
sorcery: 0.10.0 sorcery: 0.10.0
strip-indent: 3.0.0 strip-indent: 3.0.0
svelte: 3.47.0 svelte: 3.48.0
typescript: 4.6.3 typescript: 4.6.4
dev: true dev: true
/svelte-select/4.4.7: /svelte-select/4.4.7:
@ -4989,25 +4999,25 @@ packages:
} }
dev: true dev: true
/svelte/3.47.0: /svelte/3.48.0:
resolution: resolution:
{ {
integrity: sha512-4JaJp3HEoTCGARRWZQIZDUanhYv0iyoHikklVHVLH9xFE9db22g4TDv7CPeNA8HD1JgjXI1vlhR1JZvvhaTu2Q== integrity: sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ==
} }
engines: { node: '>= 8' } engines: { node: '>= 8' }
dev: true dev: true
/sveltekit-i18n/2.1.2_svelte@3.47.0: /sveltekit-i18n/2.2.1_svelte@3.48.0:
resolution: resolution:
{ {
integrity: sha512-s5YxcbNd2EWNZaZR1A4Drt8s53E4fpUkN4XIWd3VRpw1pihZVWssqmBW1qkjQ6AB0kiu1Qwule+vt1HkbQOjrg== integrity: sha512-1CyaRN6dBvp467JjBdji+nJf+7pZ3myFu+2YaCuGSAt09Cvt5ndfRbzy+aAd5WJdk6Lu/hnPEE7ZZFauTbDRNw==
} }
peerDependencies: peerDependencies:
svelte: ^3.x svelte: ^3.x
dependencies: dependencies:
'@sveltekit-i18n/base': 1.1.1_svelte@3.47.0 '@sveltekit-i18n/base': 1.2.1_svelte@3.48.0
'@sveltekit-i18n/parser-default': 1.0.3 '@sveltekit-i18n/parser-default': 1.0.3
svelte: 3.47.0 svelte: 3.48.0
dev: true dev: true
/table/6.7.2: /table/6.7.2:
@ -5057,10 +5067,10 @@ packages:
normalize-path: 3.0.0 normalize-path: 3.0.0
object-hash: 3.0.0 object-hash: 3.0.0
picocolors: 1.0.0 picocolors: 1.0.0
postcss: 8.4.12 postcss: 8.4.13
postcss-js: 4.0.0_postcss@8.4.12 postcss-js: 4.0.0_postcss@8.4.13
postcss-load-config: 3.1.4_ysmyu6g5dtd6yanj6zrab4uqoy postcss-load-config: 3.1.4_4jqnslpwnj4ifyjfqbkuebd4fy
postcss-nested: 5.0.6_postcss@8.4.12 postcss-nested: 5.0.6_postcss@8.4.13
postcss-selector-parser: 6.0.10 postcss-selector-parser: 6.0.10
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
quick-lru: 5.1.1 quick-lru: 5.1.1
@ -5133,7 +5143,7 @@ packages:
engines: { node: '>=0.10.0' } engines: { node: '>=0.10.0' }
dev: true dev: true
/ts-node/10.7.0_3z6inmgn4ud4moqealnfxgbl2m: /ts-node/10.7.0_l47be6km5p57gglrggidw5gsgm:
resolution: resolution:
{ {
integrity: sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== integrity: sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==
@ -5155,14 +5165,14 @@ packages:
'@tsconfig/node12': 1.0.9 '@tsconfig/node12': 1.0.9
'@tsconfig/node14': 1.0.1 '@tsconfig/node14': 1.0.1
'@tsconfig/node16': 1.0.2 '@tsconfig/node16': 1.0.2
'@types/node': 17.0.25 '@types/node': 17.0.31
acorn: 8.5.0 acorn: 8.5.0
acorn-walk: 8.2.0 acorn-walk: 8.2.0
arg: 4.1.3 arg: 4.1.3
create-require: 1.1.1 create-require: 1.1.1
diff: 4.0.2 diff: 4.0.2
make-error: 1.3.6 make-error: 1.3.6
typescript: 4.6.3 typescript: 4.6.4
v8-compile-cache-lib: 3.0.0 v8-compile-cache-lib: 3.0.0
yn: 3.1.1 yn: 3.1.1
dev: true dev: true
@ -5173,14 +5183,14 @@ packages:
integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
} }
/tslib/2.3.1: /tslib/2.4.0:
resolution: resolution:
{ {
integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
} }
dev: true dev: true
/tsutils/3.21.0_typescript@4.6.3: /tsutils/3.21.0_typescript@4.6.4:
resolution: resolution:
{ {
integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
@ -5190,7 +5200,7 @@ packages:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies: dependencies:
tslib: 1.14.1 tslib: 1.14.1
typescript: 4.6.3 typescript: 4.6.4
dev: true dev: true
/tweetnacl/0.14.5: /tweetnacl/0.14.5:
@ -5223,10 +5233,10 @@ packages:
engines: { node: '>=10' } engines: { node: '>=10' }
dev: true dev: true
/typescript/4.6.3: /typescript/4.6.4:
resolution: resolution:
{ {
integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==
} }
engines: { node: '>=4.2.0' } engines: { node: '>=4.2.0' }
hasBin: true hasBin: true
@ -5303,7 +5313,7 @@ packages:
optional: true optional: true
dependencies: dependencies:
esbuild: 0.14.34 esbuild: 0.14.34
postcss: 8.4.12 postcss: 8.4.13
resolve: 1.22.0 resolve: 1.22.0
rollup: 2.61.1 rollup: 2.61.1
optionalDependencies: optionalDependencies:

View File

@ -0,0 +1,24 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_PlausibleAnalytics" (
"id" TEXT NOT NULL PRIMARY KEY,
"email" TEXT,
"username" TEXT,
"password" TEXT NOT NULL,
"postgresqlUser" TEXT NOT NULL,
"postgresqlPassword" TEXT NOT NULL,
"postgresqlDatabase" TEXT NOT NULL,
"postgresqlPublicPort" INTEGER,
"secretKeyBase" TEXT,
"scriptName" TEXT NOT NULL DEFAULT 'plausible.js',
"serviceId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "PlausibleAnalytics_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_PlausibleAnalytics" ("createdAt", "email", "id", "password", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "serviceId", "updatedAt", "username") SELECT "createdAt", "email", "id", "password", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "serviceId", "updatedAt", "username" FROM "PlausibleAnalytics";
DROP TABLE "PlausibleAnalytics";
ALTER TABLE "new_PlausibleAnalytics" RENAME TO "PlausibleAnalytics";
CREATE UNIQUE INDEX "PlausibleAnalytics_serviceId_key" ON "PlausibleAnalytics"("serviceId");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View File

@ -0,0 +1,32 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Wordpress" (
"id" TEXT NOT NULL PRIMARY KEY,
"extraConfig" TEXT,
"tablePrefix" TEXT,
"ownMysql" BOOLEAN NOT NULL DEFAULT false,
"mysqlHost" TEXT,
"mysqlPort" INTEGER,
"mysqlUser" TEXT NOT NULL,
"mysqlPassword" TEXT NOT NULL,
"mysqlRootUser" TEXT NOT NULL,
"mysqlRootUserPassword" TEXT NOT NULL,
"mysqlDatabase" TEXT,
"mysqlPublicPort" INTEGER,
"ftpEnabled" BOOLEAN NOT NULL DEFAULT false,
"ftpUser" TEXT,
"ftpPassword" TEXT,
"ftpPublicPort" INTEGER,
"ftpHostKey" TEXT,
"ftpHostKeyPrivate" TEXT,
"serviceId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress";
DROP TABLE "Wordpress";
ALTER TABLE "new_Wordpress" RENAME TO "Wordpress";
CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View File

@ -322,6 +322,7 @@ model PlausibleAnalytics {
postgresqlDatabase String postgresqlDatabase String
postgresqlPublicPort Int? postgresqlPublicPort Int?
secretKeyBase String? secretKeyBase String?
scriptName String @default("plausible.js")
serviceId String @unique serviceId String @unique
service Service @relation(fields: [serviceId], references: [id]) service Service @relation(fields: [serviceId], references: [id])
createdAt DateTime @default(now()) createdAt DateTime @default(now())
@ -352,6 +353,9 @@ model Wordpress {
id String @id @default(cuid()) id String @id @default(cuid())
extraConfig String? extraConfig String?
tablePrefix String? tablePrefix String?
ownMysql Boolean @default(false)
mysqlHost String?
mysqlPort Int?
mysqlUser String mysqlUser String
mysqlPassword String mysqlPassword String
mysqlRootUser String mysqlRootUser String

View File

@ -405,7 +405,68 @@ export function setDefaultBaseImage(buildPack) {
label: 'webdevops/php-nginx:7.1-alpine' label: 'webdevops/php-nginx:7.1-alpine'
} }
]; ];
const pythonVersions = [
{
value: 'python:3.10-buster',
label: 'python:3.10-buster'
},
{
value: 'python:3.10-bullseye',
label: 'python:3.10-bullseye'
},
{
value: 'python:3.10-slim-bullseye',
label: 'python:3.10-slim-bullseye'
},
{
value: 'python:3.9-alpine',
label: 'python:3.9-alpine'
},
{
value: 'python:3.9-buster',
label: 'python:3.9-buster'
},
{
value: 'python:3.9-bullseye',
label: 'python:3.9-bullseye'
},
{
value: 'python:3.9-slim-bullseye',
label: 'python:3.9-slim-bullseye'
},
{
value: 'python:3.8-alpine',
label: 'python:3.8-alpine'
},
{
value: 'python:3.8-buster',
label: 'python:3.8-buster'
},
{
value: 'python:3.8-bullseye',
label: 'python:3.8-bullseye'
},
{
value: 'python:3.8-slim-bullseye',
label: 'python:3.8-slim-bullseye'
},
{
value: 'python:3.7-alpine',
label: 'python:3.7-alpine'
},
{
value: 'python:3.7-buster',
label: 'python:3.7-buster'
},
{
value: 'python:3.7-bullseye',
label: 'python:3.7-bullseye'
},
{
value: 'python:3.7-slim-bullseye',
label: 'python:3.7-slim-bullseye'
}
];
let payload = { let payload = {
baseImage: null, baseImage: null,
baseBuildImage: null, baseBuildImage: null,
@ -426,6 +487,7 @@ export function setDefaultBaseImage(buildPack) {
} }
if (buildPack === 'python') { if (buildPack === 'python') {
payload.baseImage = 'python:3-alpine'; payload.baseImage = 'python:3-alpine';
payload.baseImages = pythonVersions;
} }
if (buildPack === 'rust') { if (buildPack === 'rust') {
payload.baseImage = 'rust:latest'; payload.baseImage = 'rust:latest';

View File

@ -3,13 +3,88 @@
</script> </script>
<svg <svg
class={isAbsolute ? 'absolute top-0 left-0 -m-10 h-20 w-20' : 'mx-auto w-8 h-8'} viewBox="0 0 128 128"
id="Layer_1" class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
data-name="Layer 1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 361.67651 499.33603"
><path
d="M203.77731,148.85754c-10.8147-12.762-20.13269-25.8139-22.02478-28.49224a.426.426,0,0,0-.70032.00006c-1.89172,2.6784-11.20758,15.73022-22.02191,28.49218-92.69141,118.085,14.62982,197.75507,14.62982,197.75507l.87.60461c.8136,12.32624,2.83508,30.041,2.83508,30.041H185.442s2.01282-17.63849,2.83-29.96106l.87549-.68451S296.46774,266.94257,203.77731,148.85754ZM181.404,344.88123h-.001s-4.811-4.10383-6.10962-6.16l-.01172-.22131,5.81946-128.56055a.30281.30281,0,0,1,.605,0l5.81946,128.56036-.01135.22065C186.21652,340.77625,181.404,344.88123,181.404,344.88123Z"
fill="#00684a"
/></svg
> >
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#439934"
d="M88.038 42.812c1.605 4.643 2.761 9.383 3.141 14.296.472 6.095.256 12.147-1.029 18.142-.035.165-.109.32-.164.48-.403.001-.814-.049-1.208.012-3.329.523-6.655 1.065-9.981 1.604-3.438.557-6.881 1.092-10.313 1.687-1.216.21-2.721-.041-3.212 1.641-.014.046-.154.054-.235.08l.166-10.051-.169-24.252 1.602-.275c2.62-.429 5.24-.864 7.862-1.281 3.129-.497 6.261-.98 9.392-1.465 1.381-.215 2.764-.412 4.148-.618z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#45A538"
d="M61.729 110.054c-1.69-1.453-3.439-2.842-5.059-4.37-8.717-8.222-15.093-17.899-18.233-29.566-.865-3.211-1.442-6.474-1.627-9.792-.13-2.322-.318-4.665-.154-6.975.437-6.144 1.325-12.229 3.127-18.147l.099-.138c.175.233.427.439.516.702 1.759 5.18 3.505 10.364 5.242 15.551 5.458 16.3 10.909 32.604 16.376 48.9.107.318.384.579.583.866l-.87 2.969z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#46A037"
d="M88.038 42.812c-1.384.206-2.768.403-4.149.616-3.131.485-6.263.968-9.392 1.465-2.622.417-5.242.852-7.862 1.281l-1.602.275-.012-1.045c-.053-.859-.144-1.717-.154-2.576-.069-5.478-.112-10.956-.18-16.434-.042-3.429-.105-6.857-.175-10.285-.043-2.13-.089-4.261-.185-6.388-.052-1.143-.236-2.28-.311-3.423-.042-.657.016-1.319.029-1.979.817 1.583 1.616 3.178 2.456 4.749 1.327 2.484 3.441 4.314 5.344 6.311 7.523 7.892 12.864 17.068 16.193 27.433z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#409433"
d="M65.036 80.753c.081-.026.222-.034.235-.08.491-1.682 1.996-1.431 3.212-1.641 3.432-.594 6.875-1.13 10.313-1.687 3.326-.539 6.652-1.081 9.981-1.604.394-.062.805-.011 1.208-.012-.622 2.22-1.112 4.488-1.901 6.647-.896 2.449-1.98 4.839-3.131 7.182a49.142 49.142 0 01-6.353 9.763c-1.919 2.308-4.058 4.441-6.202 6.548-1.185 1.165-2.582 2.114-3.882 3.161l-.337-.23-1.214-1.038-1.256-2.753a41.402 41.402 0 01-1.394-9.838l.023-.561.171-2.426c.057-.828.133-1.655.168-2.485.129-2.982.241-5.964.359-8.946z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#4FAA41"
d="M65.036 80.753c-.118 2.982-.23 5.964-.357 8.947-.035.83-.111 1.657-.168 2.485l-.765.289c-1.699-5.002-3.399-9.951-5.062-14.913-2.75-8.209-5.467-16.431-8.213-24.642a4498.887 4498.887 0 00-6.7-19.867c-.105-.31-.407-.552-.617-.826l4.896-9.002c.168.292.39.565.496.879a6167.476 6167.476 0 016.768 20.118c2.916 8.73 5.814 17.467 8.728 26.198.116.349.308.671.491 1.062l.67-.78-.167 10.052z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#4AA73C"
d="M43.155 32.227c.21.274.511.516.617.826a4498.887 4498.887 0 016.7 19.867c2.746 8.211 5.463 16.433 8.213 24.642 1.662 4.961 3.362 9.911 5.062 14.913l.765-.289-.171 2.426-.155.559c-.266 2.656-.49 5.318-.814 7.968-.163 1.328-.509 2.632-.772 3.947-.198-.287-.476-.548-.583-.866-5.467-16.297-10.918-32.6-16.376-48.9a3888.972 3888.972 0 00-5.242-15.551c-.089-.263-.34-.469-.516-.702l3.272-8.84z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#57AE47"
d="M65.202 70.702l-.67.78c-.183-.391-.375-.714-.491-1.062-2.913-8.731-5.812-17.468-8.728-26.198a6167.476 6167.476 0 00-6.768-20.118c-.105-.314-.327-.588-.496-.879l6.055-7.965c.191.255.463.482.562.769 1.681 4.921 3.347 9.848 5.003 14.778 1.547 4.604 3.071 9.215 4.636 13.813.105.308.47.526.714.786l.012 1.045c.058 8.082.115 16.167.171 24.251z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#60B24F"
d="M65.021 45.404c-.244-.26-.609-.478-.714-.786-1.565-4.598-3.089-9.209-4.636-13.813-1.656-4.93-3.322-9.856-5.003-14.778-.099-.287-.371-.514-.562-.769 1.969-1.928 3.877-3.925 5.925-5.764 1.821-1.634 3.285-3.386 3.352-5.968.003-.107.059-.214.145-.514l.519 1.306c-.013.661-.072 1.322-.029 1.979.075 1.143.259 2.28.311 3.423.096 2.127.142 4.258.185 6.388.069 3.428.132 6.856.175 10.285.067 5.478.111 10.956.18 16.434.008.861.098 1.718.152 2.577z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#A9AA88"
d="M62.598 107.085c.263-1.315.609-2.62.772-3.947.325-2.649.548-5.312.814-7.968l.066-.01.066.011a41.402 41.402 0 001.394 9.838c-.176.232-.425.439-.518.701-.727 2.05-1.412 4.116-2.143 6.166-.1.28-.378.498-.574.744l-.747-2.566.87-2.969z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#B6B598"
d="M62.476 112.621c.196-.246.475-.464.574-.744.731-2.05 1.417-4.115 2.143-6.166.093-.262.341-.469.518-.701l1.255 2.754c-.248.352-.59.669-.728 1.061l-2.404 7.059c-.099.283-.437.483-.663.722l-.695-3.985z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#C2C1A7"
d="M63.171 116.605c.227-.238.564-.439.663-.722l2.404-7.059c.137-.391.48-.709.728-1.061l1.215 1.037c-.587.58-.913 1.25-.717 2.097l-.369 1.208c-.168.207-.411.387-.494.624-.839 2.403-1.64 4.819-2.485 7.222-.107.305-.404.544-.614.812-.109-1.387-.22-2.771-.331-4.158z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#CECDB7"
d="M63.503 120.763c.209-.269.506-.508.614-.812.845-2.402 1.646-4.818 2.485-7.222.083-.236.325-.417.494-.624l-.509 5.545c-.136.157-.333.294-.398.477-.575 1.614-1.117 3.24-1.694 4.854-.119.333-.347.627-.525.938-.158-.207-.441-.407-.454-.623-.051-.841-.016-1.688-.013-2.533z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#DBDAC7"
d="M63.969 123.919c.178-.312.406-.606.525-.938.578-1.613 1.119-3.239 1.694-4.854.065-.183.263-.319.398-.477l.012 3.64-1.218 3.124-1.411-.495z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#EBE9DC"
d="M65.38 124.415l1.218-3.124.251 3.696-1.469-.572z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#CECDB7"
d="M67.464 110.898c-.196-.847.129-1.518.717-2.097l.337.23-1.054 1.867z"
/><path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#4FAA41"
d="M64.316 95.172l-.066-.011-.066.01.155-.559-.023.56z"
/>
</svg>

View File

@ -4,6 +4,6 @@
<img <img
alt="plausible logo" alt="plausible logo"
class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-6 mx-auto'} class={isAbsolute ? 'w-9 absolute top-0 left-0 -m-4' : 'w-6 mx-auto'}
src="/plausible.png" src="/plausible.png"
/> />

View File

@ -128,9 +128,7 @@ export function findBuildPack(pack, packageManager = 'npm') {
if (pack === 'astro') { if (pack === 'astro') {
return { return {
...metaData, ...metaData,
installCommand: `yarn install`, ...defaultBuildAndDeploy(packageManager),
buildCommand: `yarn build`,
startCommand: null,
publishDirectory: `dist`, publishDirectory: `dist`,
port: 80 port: 80
}; };
@ -138,9 +136,7 @@ export function findBuildPack(pack, packageManager = 'npm') {
if (pack === 'eleventy') { if (pack === 'eleventy') {
return { return {
...metaData, ...metaData,
installCommand: `yarn install`, ...defaultBuildAndDeploy(packageManager),
buildCommand: `yarn build`,
startCommand: null,
publishDirectory: `_site`, publishDirectory: `_site`,
port: 80 port: 80
}; };

View File

@ -28,7 +28,7 @@ if (!dev) {
} }
export const prisma = new PrismaClient({ export const prisma = new PrismaClient({
errorFormat: 'pretty', errorFormat: 'minimal',
rejectOnNotFound: false rejectOnNotFound: false
}); });

View File

@ -329,7 +329,8 @@ export async function updatePlausibleAnalyticsService({
email, email,
exposePort, exposePort,
username, username,
name name,
scriptName
}: { }: {
id: string; id: string;
fqdn: string; fqdn: string;
@ -337,8 +338,12 @@ export async function updatePlausibleAnalyticsService({
name: string; name: string;
email: string; email: string;
username: string; username: string;
scriptName: string;
}): Promise<void> { }): Promise<void> {
await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } }); await prisma.plausibleAnalytics.update({
where: { serviceId: id },
data: { email, username, scriptName }
});
await prisma.service.update({ where: { id }, data: { name, fqdn, exposePort } }); await prisma.service.update({ where: { id }, data: { name, fqdn, exposePort } });
} }
@ -414,7 +419,9 @@ export async function updateWordpress({
name, name,
exposePort, exposePort,
mysqlDatabase, mysqlDatabase,
extraConfig extraConfig,
mysqlHost,
mysqlPort
}: { }: {
id: string; id: string;
fqdn: string; fqdn: string;
@ -422,10 +429,24 @@ export async function updateWordpress({
exposePort?: number; exposePort?: number;
mysqlDatabase: string; mysqlDatabase: string;
extraConfig: string; extraConfig: string;
mysqlHost?: string;
mysqlPort?: number;
}): Promise<Service> { }): Promise<Service> {
return await prisma.service.update({ return await prisma.service.update({
where: { id }, where: { id },
data: { fqdn, name, exposePort, wordpress: { update: { mysqlDatabase, extraConfig } } } data: {
fqdn,
name,
exposePort,
wordpress: {
update: {
mysqlDatabase,
extraConfig,
mysqlHost,
mysqlPort
}
}
}
}); });
} }

View File

@ -55,6 +55,9 @@ frontend http
http-request redirect location {{{redirectValue}}} code ${ http-request redirect location {{{redirectValue}}} code ${
dev ? 302 : 301 dev ? 302 : 301
} if { req.hdr(host) -i {{redirectTo}} } } if { req.hdr(host) -i {{redirectTo}} }
{{#scriptName}}
http-request set-path /js/plausible.js if { hdr(host) -i {{domain}} } { path_beg -i /js/{{scriptName}} }
{{/scriptName}}
{{/services}} {{/services}}
{{#coolify}} {{#coolify}}
@ -218,7 +221,15 @@ export async function configureHAProxy(): Promise<void> {
const services = await listServicesWithIncludes(); const services = await listServicesWithIncludes();
for (const service of services) { for (const service of services) {
const { fqdn, id, type, destinationDocker, destinationDockerId, updatedAt } = service; const {
fqdn,
id,
type,
destinationDocker,
destinationDockerId,
updatedAt,
plausibleAnalytics
} = service;
if (destinationDockerId) { if (destinationDockerId) {
const { engine } = destinationDocker; const { engine } = destinationDocker;
const found = supportedServiceTypesAndVersions.find((a) => a.name === type); const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
@ -232,6 +243,12 @@ export async function configureHAProxy(): Promise<void> {
const isWWW = fqdn.includes('www.'); const isWWW = fqdn.includes('www.');
const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
if (isRunning) { if (isRunning) {
// Plausible Analytics custom script
let scriptName = false;
if (type === 'plausibleanalytics' && plausibleAnalytics.scriptName !== 'plausible.js') {
scriptName = plausibleAnalytics.scriptName;
}
data.services.push({ data.services.push({
id, id,
port, port,
@ -241,7 +258,8 @@ export async function configureHAProxy(): Promise<void> {
isHttps, isHttps,
redirectValue, redirectValue,
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain, redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain,
updatedAt: updatedAt.getTime() updatedAt: updatedAt.getTime(),
scriptName
}); });
} }
} }

39
src/routes/_Trend.svelte Normal file
View File

@ -0,0 +1,39 @@
<script lang="ts">
export let trend;
</script>
{#if trend === 'up'}
<span class="-mt-1 inline-flex px-2 text-green-500">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="17" y1="7" x2="7" y2="17" />
<polyline points="8 7 17 7 17 16" />
</svg></span
>
{:else if trend === 'down'}
<span class="text-red-500 px-2 inline-flex -mt-1">
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-8 h-8"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="7" y1="7" x2="17" y2="17" />
<polyline points="17 8 17 17 8 17" />
</svg>
</span>
{/if}

View File

@ -78,6 +78,7 @@
} }
} }
}); });
async function logout() { async function logout() {
try { try {
await del(`/logout.json`, {}); await del(`/logout.json`, {});
@ -137,7 +138,7 @@
{#if !$session.whiteLabeled} {#if !$session.whiteLabeled}
<div class="my-4 h-10 w-10"><img src="/favicon.png" alt="coolLabs logo" /></div> <div class="my-4 h-10 w-10"><img src="/favicon.png" alt="coolLabs logo" /></div>
{/if} {/if}
<div class="flex flex-col space-y-4 py-2" class:mt-2={$session.whiteLabeled}> <div class="flex flex-col space-y-2 py-2" class:mt-2={$session.whiteLabeled}>
<a <a
sveltekit:prefetch sveltekit:prefetch
href="/" href="/"
@ -222,7 +223,6 @@
<polyline points="10 15 13 18 10 21" /> <polyline points="10 15 13 18 10 21" />
</svg> </svg>
</a> </a>
<div class="border-t border-stone-700" />
<a <a
sveltekit:prefetch sveltekit:prefetch
href="/destinations" href="/destinations"
@ -284,7 +284,6 @@
<path d="M4 12v6a8 3 0 0 0 16 0v-6" /> <path d="M4 12v6a8 3 0 0 0 16 0v-6" />
</svg> </svg>
</a> </a>
<div class="border-t border-stone-700" />
<a <a
sveltekit:prefetch sveltekit:prefetch
href="/services" href="/services"
@ -423,7 +422,7 @@
{/if} {/if}
{/if} {/if}
</div> </div>
<div class="flex flex-col space-y-4 py-2"> <div class="flex flex-col space-y-2 py-2">
<a <a
sveltekit:prefetch sveltekit:prefetch
href="/iam" href="/iam"

View File

@ -358,24 +358,26 @@
/> />
</div> </div>
</div> </div>
<div class="grid grid-cols-2 items-center"> {#if application.buildPack !== 'docker'}
<label for="baseImage" class="text-base font-bold text-stone-100" <div class="grid grid-cols-2 items-center">
>{$t('application.base_image')}</label <label for="baseImage" class="text-base font-bold text-stone-100"
> >{$t('application.base_image')}</label
<div class="custom-select-wrapper"> >
<Select <div class="custom-select-wrapper">
isDisabled={!$session.isAdmin || isRunning} <Select
containerClasses={containerClass()} isDisabled={!$session.isAdmin || isRunning}
id="baseImages" containerClasses={containerClass()}
showIndicator={!isRunning} id="baseImages"
items={application.baseImages} showIndicator={!isRunning}
on:select={selectBaseImage} items={application.baseImages}
value={application.baseImage} on:select={selectBaseImage}
isClearable={false} value={application.baseImage}
/> isClearable={false}
/>
</div>
<Explainer text={$t('application.base_image_explainer')} />
</div> </div>
<Explainer text={$t('application.base_image_explainer')} /> {/if}
</div>
{#if application.buildCommand || application.buildPack === 'rust' || application.buildPack === 'laravel'} {#if application.buildCommand || application.buildPack === 'rust' || application.buildPack === 'laravel'}
<div class="grid grid-cols-2 items-center pb-8"> <div class="grid grid-cols-2 items-center pb-8">
<label for="baseBuildImage" class="text-base font-bold text-stone-100" <label for="baseBuildImage" class="text-base font-bold text-stone-100"
@ -525,7 +527,8 @@
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label> <label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
<input <input
readonly={!$session.isAdmin} readonly={!$session.isAdmin && !isRunning}
disabled={!$session.isAdmin || isRunning}
name="exposePort" name="exposePort"
id="exposePort" id="exposePort"
bind:value={application.exposePort} bind:value={application.exposePort}

View File

@ -1,4 +1,8 @@
<div class="lds-ripple absolute left-0"> <script>
export let position = 'absolute';
</script>
<div class="lds-ripple {position} left-0">
<div /> <div />
<div /> <div />
</div> </div>

View File

@ -60,7 +60,7 @@
</div> </div>
{/if} {/if}
</div> </div>
<div class="flex flex-col flex-wrap justify-center"> <div class="flex justify-center">
{#if !applications || ownApplications.length === 0} {#if !applications || ownApplications.length === 0}
<div class="flex-col"> <div class="flex-col">
<div class="text-center text-xl font-bold">{$t('application.no_applications_found')}</div> <div class="text-center text-xl font-bold">{$t('application.no_applications_found')}</div>

View File

@ -2,45 +2,67 @@ import { getUserDetails } from '$lib/common';
import * as db from '$lib/database'; import * as db from '$lib/database';
import { ErrorHandler } from '$lib/database'; import { ErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
import os from 'node:os';
import osu from 'node-os-utils';
export const get: RequestHandler = async (event) => { export const get: RequestHandler = async (event) => {
const { userId, teamId, status, body } = await getUserDetails(event); const { userId, teamId, status, body } = await getUserDetails(event);
if (status === 401) return { status, body }; if (status === 401) return { status, body };
const usage = event.url.searchParams.get('usage');
try { if (usage) {
const applicationsCount = await db.prisma.application.count({ try {
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } } return {
}); status: 200,
const sourcesCount = await db.prisma.gitSource.count({ body: {
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } } uptime: os.uptime(),
}); memory: await osu.mem.info(),
const destinationsCount = await db.prisma.destinationDocker.count({ cpu: {
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } } load: os.loadavg(),
}); usage: await osu.cpu.usage(),
const teamsCount = await db.prisma.permission.count({ where: { userId } }); count: os.cpus().length
const databasesCount = await db.prisma.database.count({ },
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } } disk: await osu.drive.info()
}); }
const servicesCount = await db.prisma.service.count({ };
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } } } catch (error) {
}); return ErrorHandler(error);
const teams = await db.prisma.permission.findMany({ }
where: { userId }, } else {
include: { team: { include: { _count: { select: { users: true } } } } } try {
}); const applicationsCount = await db.prisma.application.count({
return { where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
body: { });
teams, const sourcesCount = await db.prisma.gitSource.count({
applicationsCount, where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
sourcesCount, });
destinationsCount, const destinationsCount = await db.prisma.destinationDocker.count({
teamsCount, where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
databasesCount, });
servicesCount const teamsCount = await db.prisma.permission.count({ where: { userId } });
} const databasesCount = await db.prisma.database.count({
}; where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
} catch (error) { });
return ErrorHandler(error); const servicesCount = await db.prisma.service.count({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
});
const teams = await db.prisma.permission.findMany({
where: { userId },
include: { team: { include: { _count: { select: { users: true } } } } }
});
return {
body: {
teams,
applicationsCount,
sourcesCount,
destinationsCount,
teamsCount,
databasesCount,
servicesCount
}
};
} catch (error) {
return ErrorHandler(error);
}
} }
}; };

View File

@ -47,7 +47,7 @@
</div> </div>
</div> </div>
<div class="flex flex-col flex-wrap justify-center"> <div class="flex justify-center">
{#if !databases || ownDatabases.length === 0} {#if !databases || ownDatabases.length === 0}
<div class="flex-col"> <div class="flex-col">
<div class="text-center text-xl font-bold">{$t('database.no_databases_found')}</div> <div class="text-center text-xl font-bold">{$t('database.no_databases_found')}</div>

View File

@ -39,10 +39,13 @@
import { t } from '$lib/translations'; import { t } from '$lib/translations';
</script> </script>
<div class="flex space-x-1 p-6 text-2xl font-bold"> <div class="flex h-20 items-center space-x-2 p-5 px-6 font-bold">
<div class="tracking-tight">{$t('application.destination')}</div> <div class="-mb-5 flex-col">
<span class="arrow-right-applications px-1">></span> <div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
<span class="pr-2">{destination.name}</span> Configuration
</div>
<span class="text-xs">{destination.name}</span>
</div>
</div> </div>
<div class="mx-auto max-w-4xl px-6"> <div class="mx-auto max-w-4xl px-6">

View File

@ -21,6 +21,11 @@
<script lang="ts"> <script lang="ts">
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { get } from '$lib/api';
import { onDestroy, onMount } from 'svelte';
import Loading from './applications/[id]/logs/_Loading.svelte';
import Trend from './_Trend.svelte';
import { session } from '$app/stores';
export let applicationsCount: number; export let applicationsCount: number;
export let sourcesCount: number; export let sourcesCount: number;
@ -28,89 +33,261 @@
export let teamsCount: number; export let teamsCount: number;
export let databasesCount: number; export let databasesCount: number;
export let servicesCount: number; export let servicesCount: number;
let loading = {
usage: false
};
let usageInterval = null;
let memoryWarning = false;
let cpuWarning = false;
let diskWarning = false;
let trends = {
memory: 'stable',
cpu: 'stable',
disk: 'stable'
};
let usage = {
cpu: {
load: [0, 0, 0],
count: 0,
usage: 0
},
memory: {
totalMemMb: 0,
freeMemMb: 0,
usedMemMb: 0,
freeMemPercentage: 0
},
disk: {
freePercentage: 0,
totalGb: 0,
usedGb: 0
}
};
async function getStatus() {
if (loading.usage) return;
try {
loading.usage = true;
const data = await get(`/dashboard.json?usage=true`);
if (data.memory.freeMemPercentage === usage.memory.freeMemPercentage) {
trends.memory = 'stable';
} else {
if (data.memory.freeMemPercentage > usage.memory.freeMemPercentage) {
trends.memory = 'up';
} else {
trends.memory = 'down';
}
}
if (data.cpu.usage === usage.cpu.usage) {
trends.cpu = 'stable';
} else {
if (data.cpu.usage > usage.cpu.usage) {
trends.cpu = 'up';
} else {
trends.cpu = 'down';
}
}
if (data.disk.freePercentage === usage.disk.freePercentage) {
trends.disk = 'stable';
} else {
if (data.disk.freePercentage > usage.disk.freePercentage) {
trends.disk = 'up';
} else {
trends.disk = 'down';
}
}
usage = data;
if (usage.memory.freeMemPercentage < 15) {
memoryWarning = true;
} else {
memoryWarning = false;
}
if (usage.cpu.usage > 90) {
cpuWarning = true;
} else {
cpuWarning = false;
}
if (usage.disk.freePercentage < 10) {
diskWarning = true;
} else {
diskWarning = false;
}
} catch (error) {
} finally {
loading.usage = false;
}
}
onDestroy(() => {
clearInterval(usageInterval);
});
onMount(async () => {
if ($session.teamId === '0') {
await getStatus();
usageInterval = setInterval(async () => {
await getStatus();
}, 1000);
}
});
</script> </script>
<div class="flex space-x-1 p-6 font-bold"> <div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">{$t('index.dashboard')}</div> <div class="mr-4 text-2xl tracking-tight">{$t('index.dashboard')}</div>
</div> </div>
<div class="mt-10 pb-12 tracking-tight sm:pb-16"> <div class="mt-10 pb-12 tracking-tight sm:pb-16">
<div class="relative"> <div class="mx-auto max-w-4xl">
<div class="absolute inset-0 h-1/2" /> {#if $session.teamId === '0'}
<div class="relative mx-auto px-4 sm:px-6 lg:px-8"> <div class="px-6 text-2xl font-bold">Server Usage</div>
<div class="mx-auto max-w-4xl"> <dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
<dl class="gap-5 gap-y-16 sm:grid sm:grid-cols-3"> <Loading />
<a <div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
href="/applications" <dt class="truncate text-sm font-medium text-white">Total Memory</dt>
sveltekit:prefetch <dd class="mt-1 text-3xl font-semibold text-white">
class="flex cursor-pointer flex-col rounded p-6 text-center text-green-500 no-underline transition duration-150 hover:bg-green-500 hover:text-white" {(usage?.memory.totalMemMb).toFixed(0)}<span class="text-sm">MB</span>
> </dd>
<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white"> </div>
{$t('index.applications')}
</dt>
<dd class="order-1 text-5xl font-extrabold ">
{applicationsCount}
</dd>
</a>
<a
href="/destinations"
sveltekit:prefetch
class="flex cursor-pointer flex-col rounded p-6 text-center text-sky-500 no-underline transition duration-150 hover:bg-sky-500 hover:text-white"
>
<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
{$t('index.destinations')}
</dt>
<dd class="order-1 text-5xl font-extrabold ">
{destinationsCount}
</dd>
</a>
<a
href="/sources"
sveltekit:prefetch
class="flex cursor-pointer flex-col rounded p-6 text-center text-orange-500 no-underline transition duration-150 hover:bg-orange-500 hover:text-white"
>
<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
{$t('index.git_sources')}
</dt>
<dd class="order-1 text-5xl font-extrabold ">
{sourcesCount}
</dd>
</a>
<a
href="/databases"
sveltekit:prefetch
class="flex cursor-pointer flex-col rounded p-6 text-center text-purple-500 no-underline transition duration-150 hover:bg-purple-500 hover:text-white"
>
<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
{$t('index.databases')}
</dt>
<dd class="order-1 text-5xl font-extrabold ">{databasesCount}</dd>
</a>
<a
href="/services"
sveltekit:prefetch
class="flex cursor-pointer flex-col rounded p-6 text-center text-pink-500 no-underline transition duration-150 hover:bg-pink-500 hover:text-white"
>
<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
{$t('index.services')}
</dt>
<dd class="order-1 text-5xl font-extrabold ">{servicesCount}</dd>
</a>
<a <div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
href="/iam" <dt class="truncate text-sm font-medium text-white">Used Memory</dt>
sveltekit:prefetch <dd class="mt-1 text-3xl font-semibold text-white ">
class="flex cursor-pointer flex-col rounded p-6 text-center text-cyan-500 no-underline transition duration-150 hover:bg-cyan-500 hover:text-white" {(usage?.memory.usedMemMb).toFixed(0)}<span class="text-sm">MB</span>
> </dd>
<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white"> </div>
{$t('index.teams')}
</dt> <div
<dd class="order-1 text-5xl font-extrabold "> class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left"
{teamsCount} class:bg-red-500={memoryWarning}
</dd> >
</a> <dt class="truncate text-sm font-medium text-white">Free Memory</dt>
</dl> <dd class="mt-1 text-3xl font-semibold text-white">
</div> {usage?.memory.freeMemPercentage}<span class="text-sm">%</span>
</div> {#if !memoryWarning}
<Trend trend={trends.memory} />
{/if}
</dd>
</div>
</dl>
<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
<dt class="truncate text-sm font-medium text-white">Total CPUs</dt>
<dd class="mt-1 text-3xl font-semibold text-white">
{usage?.cpu.count}
</dd>
</div>
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
<dt class="truncate text-sm font-medium text-white">Load Average (5/10/30mins)</dt>
<dd class="mt-1 text-3xl font-semibold text-white">
{usage?.cpu.load.join('/')}
</dd>
</div>
<div
class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left"
class:bg-red-500={cpuWarning}
>
<dt class="truncate text-sm font-medium text-white">CPU Usage</dt>
<dd class="mt-1 text-3xl font-semibold text-white">
{usage?.cpu.usage}<span class="text-sm">%</span>
{#if !cpuWarning}
<Trend trend={trends.cpu} />
{/if}
</dd>
</div>
</dl>
<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
<dt class="truncate text-sm font-medium text-white">Total Disk</dt>
<dd class="mt-1 text-3xl font-semibold text-white">
{usage?.disk.totalGb}<span class="text-sm">GB</span>
</dd>
</div>
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
<dt class="truncate text-sm font-medium text-white">Used Disk</dt>
<dd class="mt-1 text-3xl font-semibold text-white">
{usage?.disk.usedGb}<span class="text-sm">GB</span>
</dd>
</div>
<div
class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left"
class:bg-red-500={diskWarning}
>
<dt class="truncate text-sm font-medium text-white">Free Disk</dt>
<dd class="mt-1 text-3xl font-semibold text-white">
{usage?.disk.freePercentage}<span class="text-sm">%</span>
{#if !diskWarning}
<Trend trend={trends.disk} />
{/if}
</dd>
</div>
</dl>
<div class="px-6 pt-20 text-2xl font-bold">Resources</div>
{/if}
<dl class="mt-5 grid grid-cols-1 gap-5 px-2 sm:grid-cols-3">
<a
href="/applications"
sveltekit:prefetch
class="overflow-hidden rounded px-4 py-5 text-center text-green-500 no-underline transition-all duration-100 hover:bg-green-500 hover:text-white sm:p-6 sm:text-left"
>
<dt class="truncate text-sm font-medium text-white">{$t('index.applications')}</dt>
<dd class="mt-1 text-3xl font-semibold ">
{applicationsCount}
</dd>
</a>
<a
href="/destinations"
sveltekit:prefetch
class="overflow-hidden rounded px-4 py-5 text-center text-sky-500 no-underline transition-all duration-100 hover:bg-sky-500 hover:text-white sm:p-6 sm:text-left"
>
<dt class="truncate text-sm font-medium text-white">{$t('index.destinations')}</dt>
<dd class="mt-1 text-3xl font-semibold ">
{destinationsCount}
</dd>
</a>
<a
href="/sources"
sveltekit:prefetch
class="overflow-hidden rounded px-4 py-5 text-center text-orange-500 no-underline transition-all duration-100 hover:bg-orange-500 hover:text-white sm:p-6 sm:text-left"
>
<dt class="truncate text-sm font-medium text-white">{$t('index.git_sources')}</dt>
<dd class="mt-1 text-3xl font-semibold">
{sourcesCount}
</dd>
</a>
</dl>
<dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
<a
href="/databases"
sveltekit:prefetch
class="overflow-hidden rounded px-4 py-5 text-center text-purple-500 no-underline transition-all duration-100 hover:bg-purple-500 hover:text-white sm:p-6 sm:text-left"
>
<dt class="truncate text-sm font-medium text-white">{$t('index.databases')}</dt>
<dd class="mt-1 text-3xl font-semibold ">
{databasesCount}
</dd>
</a>
<a
href="/services"
sveltekit:prefetch
class="overflow-hidden rounded px-4 py-5 text-center text-pink-500 no-underline transition-all duration-100 hover:bg-pink-500 hover:text-white sm:p-6 sm:text-left"
>
<dt class="truncate text-sm font-medium text-white">{$t('index.services')}</dt>
<dd class="mt-1 text-3xl font-semibold ">
{servicesCount}
</dd>
</a>
<a
href="/iam"
sveltekit:prefetch
class="overflow-hidden rounded px-4 py-5 text-center text-cyan-500 no-underline transition-all duration-100 hover:bg-cyan-500 hover:text-white sm:p-6 sm:text-left"
>
<dt class="truncate text-sm font-medium text-white">{$t('index.teams')}</dt>
<dd class="mt-1 text-3xl font-semibold ">
{teamsCount}
</dd>
</a>
</dl>
</div> </div>
</div> </div>

View File

@ -1,13 +1,32 @@
<script lang="ts"> <script lang="ts">
import { session } from '$app/stores';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
export let service; export let service;
export let readOnly; export let readOnly;
export let isRunning;
</script> </script>
<div class="flex space-x-1 py-5 font-bold"> <div class="flex space-x-1 py-5 font-bold">
<div class="title">Plausible Analytics</div> <div class="title">Plausible Analytics</div>
</div> </div>
<div class="grid grid-cols-2 items-center px-10">
<label for="scriptName">Script Name</label>
<input
name="scriptName"
id="scriptName"
readonly={!$session.isAdmin && !isRunning}
disabled={!$session.isAdmin || isRunning}
placeholder="plausible.js"
bind:value={service.plausibleAnalytics.scriptName}
required
/>
<Explainer
text="Useful if you would like to rename the collector script to prevent it blocked by AdBlockers."
/>
</div>
<div class="grid grid-cols-2 items-center px-10"> <div class="grid grid-cols-2 items-center px-10">
<label for="email">{$t('forms.email')}</label> <label for="email">{$t('forms.email')}</label>
<input <input
@ -77,16 +96,3 @@
disabled disabled
/> />
</div> </div>
<!-- <div class="grid grid-cols-3 items-center">
<label for="postgresqlPublicPort">Public Port</label>
<div class="col-span-2 ">
<CopyPasswordField
placeholder="{ $t('forms.generated_automatically_after_start') }"
readonly
disabled
id="postgresqlPublicPort"
name="postgresqlPublicPort"
value={service.plausibleAnalytics.postgresqlPublicPort}
/>
</div>
</div> -->

View File

@ -27,7 +27,6 @@
let loading = false; let loading = false;
let loadingVerification = false; let loadingVerification = false;
let dualCerts = service.dualCerts; let dualCerts = service.dualCerts;
let showExposePort = service.exposePort !== null;
async function handleSubmit() { async function handleSubmit() {
loading = true; loading = true;
@ -161,34 +160,23 @@
on:click={() => !isRunning && changeSettings('dualCerts')} on:click={() => !isRunning && changeSettings('dualCerts')}
/> />
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center px-10">
<Setting <label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
isCenter={false} <input
bind:setting={showExposePort} readonly={!$session.isAdmin && !isRunning}
on:click={() => { disabled={!$session.isAdmin || isRunning}
showExposePort = !showExposePort; name="exposePort"
service.exposePort = undefined; id="exposePort"
}} bind:value={service.exposePort}
title={$t('application.expose_a_port')} placeholder="12345"
description="Expose a port to the host system" />
<Explainer
text={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
/> />
</div> </div>
{#if showExposePort}
<div class="grid grid-cols-2 items-center">
<label for="exposePort" class="text-base font-bold text-stone-100">Expose Port</label>
<input
readonly={!$session.isAdmin}
name="exposePort"
id="exposePort"
bind:value={service.exposePort}
placeholder="12345"
/>
</div>
{/if}
{#if service.type === 'plausibleanalytics'} {#if service.type === 'plausibleanalytics'}
<PlausibleAnalytics bind:service {readOnly} /> <PlausibleAnalytics bind:service {isRunning} {readOnly} />
{:else if service.type === 'minio'} {:else if service.type === 'minio'}
<MinIo {service} /> <MinIo {service} />
{:else if service.type === 'vscodeserver'} {:else if service.type === 'vscodeserver'}

View File

@ -18,6 +18,7 @@
let ftpUser = service.wordpress.ftpUser; let ftpUser = service.wordpress.ftpUser;
let ftpPassword = service.wordpress.ftpPassword; let ftpPassword = service.wordpress.ftpPassword;
let ftpLoading = false; let ftpLoading = false;
let ownMysql = service.wordpress.ownMysql;
function generateUrl(publicPort) { function generateUrl(publicPort) {
return browser return browser
@ -40,7 +41,7 @@
publicPort, publicPort,
ftpUser: user, ftpUser: user,
ftpPassword: password ftpPassword: password
} = await post(`/services/${id}/wordpress/settings.json`, { } = await post(`/services/${id}/wordpress/ftp.json`, {
ftpEnabled ftpEnabled
}); });
ftpUrl = generateUrl(publicPort); ftpUrl = generateUrl(publicPort);
@ -52,6 +53,18 @@
} finally { } finally {
ftpLoading = false; ftpLoading = false;
} }
} else {
try {
if (name === 'ownMysql') {
ownMysql = !ownMysql;
}
await post(`/services/${id}/wordpress/settings.json`, {
ownMysql
});
service.wordpress.ownMysql = ownMysql;
} catch ({ error }) {
return errorNotification(error);
}
} }
} }
</script> </script>
@ -106,51 +119,95 @@ define('SUBDOMAIN_INSTALL', false);`
<div class="flex space-x-1 py-5 font-bold"> <div class="flex space-x-1 py-5 font-bold">
<div class="title">MySQL</div> <div class="title">MySQL</div>
</div> </div>
<div class="grid grid-cols-2 items-center px-10">
<Setting
dataTooltip={$t('forms.must_be_stopped_to_modify')}
bind:setting={service.wordpress.ownMysql}
disabled={isRunning}
on:click={() => !isRunning && changeSettings('ownMysql')}
title="Use your own MySQL server"
description="Enables the use of your own MySQL server. If you don't have one, you can use the one provided by Coolify."
/>
</div>
{#if service.wordpress.ownMysql}
<div class="grid grid-cols-2 items-center px-10">
<label for="mysqlHost">Host</label>
<input
name="mysqlHost"
id="mysqlHost"
required
readonly={isRunning}
disabled={isRunning}
bind:value={service.wordpress.mysqlHost}
placeholder="{$t('forms.eg')}: db.coolify.io"
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="mysqlPort">Port</label>
<input
name="mysqlPort"
id="mysqlPort"
required
readonly={isRunning}
disabled={isRunning}
bind:value={service.wordpress.mysqlPort}
placeholder="{$t('forms.eg')}: 3306"
/>
</div>
{/if}
<div class="grid grid-cols-2 items-center px-10"> <div class="grid grid-cols-2 items-center px-10">
<label for="mysqlDatabase">{$t('index.database')}</label> <label for="mysqlDatabase">{$t('index.database')}</label>
<input <input
name="mysqlDatabase" name="mysqlDatabase"
id="mysqlDatabase" id="mysqlDatabase"
required required
readonly={readOnly} readonly={readOnly && !service.wordpress.ownMysql}
disabled={readOnly} disabled={readOnly && !service.wordpress.ownMysql}
bind:value={service.wordpress.mysqlDatabase} bind:value={service.wordpress.mysqlDatabase}
placeholder="{$t('forms.eg')}: wordpress_db" placeholder="{$t('forms.eg')}: wordpress_db"
/> />
</div> </div>
<div class="grid grid-cols-2 items-center px-10"> {#if !service.wordpress.ownMysql}
<label for="mysqlRootUser">{$t('forms.root_user')}</label> <div class="grid grid-cols-2 items-center px-10">
<input <label for="mysqlRootUser">{$t('forms.root_user')}</label>
name="mysqlRootUser" <input
id="mysqlRootUser" name="mysqlRootUser"
placeholder="MySQL {$t('forms.root_user')}" id="mysqlRootUser"
value={service.wordpress.mysqlRootUser} placeholder="MySQL {$t('forms.root_user')}"
disabled value={service.wordpress.mysqlRootUser}
readonly readonly={isRunning || !service.wordpress.ownMysq}
/> disabled={isRunning || !service.wordpress.ownMysq}
</div> />
<div class="grid grid-cols-2 items-center px-10"> </div>
<label for="mysqlRootUserPassword">{$t('forms.roots_password')}</label> <div class="grid grid-cols-2 items-center px-10">
<CopyPasswordField <label for="mysqlRootUserPassword">{$t('forms.roots_password')}</label>
id="mysqlRootUserPassword" <CopyPasswordField
isPasswordField id="mysqlRootUserPassword"
readonly isPasswordField
disabled readonly={isRunning || !service.wordpress.ownMysq}
name="mysqlRootUserPassword" disabled={isRunning || !service.wordpress.ownMysq}
value={service.wordpress.mysqlRootUserPassword} name="mysqlRootUserPassword"
/> value={service.wordpress.mysqlRootUserPassword}
</div> />
</div>
{/if}
<div class="grid grid-cols-2 items-center px-10"> <div class="grid grid-cols-2 items-center px-10">
<label for="mysqlUser">{$t('forms.user')}</label> <label for="mysqlUser">{$t('forms.user')}</label>
<input name="mysqlUser" id="mysqlUser" value={service.wordpress.mysqlUser} disabled readonly /> <input
name="mysqlUser"
id="mysqlUser"
value={service.wordpress.mysqlUser}
readonly={isRunning || !service.wordpress.ownMysql}
disabled={isRunning || !service.wordpress.ownMysql}
/>
</div> </div>
<div class="grid grid-cols-2 items-center px-10"> <div class="grid grid-cols-2 items-center px-10">
<label for="mysqlPassword">{$t('forms.password')}</label> <label for="mysqlPassword">{$t('forms.password')}</label>
<CopyPasswordField <CopyPasswordField
id="mysqlPassword" id="mysqlPassword"
isPasswordField isPasswordField
readonly readonly={isRunning || !service.wordpress.ownMysql}
disabled disabled={isRunning || !service.wordpress.ownMysql}
name="mysqlPassword" name="mysqlPassword"
value={service.wordpress.mysqlPassword} value={service.wordpress.mysqlPassword}
/> />

View File

@ -13,6 +13,7 @@ import { ErrorHandler, getServiceImage } from '$lib/database';
import { makeLabelForServices } from '$lib/buildPacks/common'; import { makeLabelForServices } from '$lib/buildPacks/common';
import type { ComposeFile } from '$lib/types/composeFile'; import type { ComposeFile } from '$lib/types/composeFile';
import type { Service, DestinationDocker, Prisma } from '@prisma/client'; import type { Service, DestinationDocker, Prisma } from '@prisma/client';
import { getServiceMainPort } from '$lib/components/common';
export const post: RequestHandler = async (event) => { export const post: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event); const { teamId, status, body } = await getUserDetails(event);
@ -30,6 +31,7 @@ export const post: RequestHandler = async (event) => {
destinationDockerId, destinationDockerId,
destinationDocker, destinationDocker,
serviceSecret, serviceSecret,
exposePort,
fider: { fider: {
postgresqlUser, postgresqlUser,
postgresqlPassword, postgresqlPassword,
@ -48,6 +50,7 @@ export const post: RequestHandler = async (event) => {
} = service; } = service;
const network = destinationDockerId && destinationDocker.network; const network = destinationDockerId && destinationDocker.network;
const host = getEngine(destinationDocker.engine); const host = getEngine(destinationDocker.engine);
const port = getServiceMainPort('fider');
const { workdir } = await createDirectories({ repository: type, buildId: id }); const { workdir } = await createDirectories({ repository: type, buildId: id });
const image = getServiceImage(type); const image = getServiceImage(type);
@ -97,6 +100,7 @@ export const post: RequestHandler = async (event) => {
volumes: [], volumes: [],
restart: 'always', restart: 'always',
labels: makeLabelForServices('fider'), labels: makeLabelForServices('fider'),
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
deploy: { deploy: {
restart_policy: { restart_policy: {
condition: 'on-failure', condition: 'on-failure',

View File

@ -7,6 +7,7 @@ import { ErrorHandler, getServiceImage } from '$lib/database';
import { makeLabelForServices } from '$lib/buildPacks/common'; import { makeLabelForServices } from '$lib/buildPacks/common';
import type { ComposeFile } from '$lib/types/composeFile'; import type { ComposeFile } from '$lib/types/composeFile';
import type { Service, DestinationDocker, Prisma } from '@prisma/client'; import type { Service, DestinationDocker, Prisma } from '@prisma/client';
import { getServiceMainPort } from '$lib/components/common';
export const post: RequestHandler = async (event) => { export const post: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event); const { teamId, status, body } = await getUserDetails(event);
@ -23,10 +24,12 @@ export const post: RequestHandler = async (event) => {
destinationDockerId, destinationDockerId,
destinationDocker, destinationDocker,
serviceSecret, serviceSecret,
exposePort,
hasura: { postgresqlUser, postgresqlPassword, postgresqlDatabase } hasura: { postgresqlUser, postgresqlPassword, postgresqlDatabase }
} = service; } = service;
const network = destinationDockerId && destinationDocker.network; const network = destinationDockerId && destinationDocker.network;
const host = getEngine(destinationDocker.engine); const host = getEngine(destinationDocker.engine);
const port = getServiceMainPort('hasura');
const { workdir } = await createDirectories({ repository: type, buildId: id }); const { workdir } = await createDirectories({ repository: type, buildId: id });
const image = getServiceImage(type); const image = getServiceImage(type);
@ -65,6 +68,7 @@ export const post: RequestHandler = async (event) => {
volumes: [], volumes: [],
restart: 'always', restart: 'always',
labels: makeLabelForServices('hasura'), labels: makeLabelForServices('hasura'),
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
deploy: { deploy: {
restart_policy: { restart_policy: {
condition: 'on-failure', condition: 'on-failure',

View File

@ -65,7 +65,7 @@ export const post: RequestHandler = async (event) => {
networks: [network], networks: [network],
volumes: [config.volume], volumes: [config.volume],
restart: 'always', restart: 'always',
...(exposePort && { ports: [`${port}:${port}`] }), ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('minio'), labels: makeLabelForServices('minio'),
deploy: { deploy: {
restart_policy: { restart_policy: {

View File

@ -48,6 +48,7 @@ export const post: RequestHandler = async (event) => {
environment: config.environmentVariables, environment: config.environmentVariables,
restart: 'always', restart: 'always',
labels: makeLabelForServices('n8n'), labels: makeLabelForServices('n8n'),
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
deploy: { deploy: {
restart_policy: { restart_policy: {
condition: 'on-failure', condition: 'on-failure',

View File

@ -12,15 +12,28 @@ export const post: RequestHandler = async (event) => {
name, name,
fqdn, fqdn,
exposePort, exposePort,
plausibleAnalytics: { email, username } plausibleAnalytics: { email, username, scriptName }
} = await event.request.json(); } = await event.request.json();
if (fqdn) fqdn = fqdn.toLowerCase(); if (fqdn) fqdn = fqdn.toLowerCase();
if (email) email = email.toLowerCase(); if (email) email = email.toLowerCase();
if (exposePort) exposePort = Number(exposePort); if (exposePort) exposePort = Number(exposePort);
if (scriptName) {
scriptName = scriptName.toLowerCase();
if (scriptName.startsWith('/')) {
scriptName = scriptName.replaceAll(/\//gi, '');
}
}
try { try {
await db.updatePlausibleAnalyticsService({ id, fqdn, name, email, username, exposePort }); await db.updatePlausibleAnalyticsService({
id,
fqdn,
name,
email,
username,
exposePort,
scriptName
});
return { status: 201 }; return { status: 201 };
} catch (error) { } catch (error) {
return ErrorHandler(error); return ErrorHandler(error);

View File

@ -135,7 +135,7 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
networks: [network], networks: [network],
environment: config.plausibleAnalytics.environmentVariables, environment: config.plausibleAnalytics.environmentVariables,
restart: 'always', restart: 'always',
...(exposePort && { ports: [`${port}:${exposePort}`] }), ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
depends_on: [`${id}-postgresql`, `${id}-clickhouse`], depends_on: [`${id}-postgresql`, `${id}-clickhouse`],
labels: makeLabelForServices('plausibleAnalytics'), labels: makeLabelForServices('plausibleAnalytics'),
deploy: { deploy: {
@ -195,6 +195,7 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
} }
}; };
const composeFileDestination = `${workdir}/docker-compose.yaml`; const composeFileDestination = `${workdir}/docker-compose.yaml`;
console.log(JSON.stringify(composeFile, null, 2));
await fs.writeFile(composeFileDestination, yaml.dump(composeFile)); await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`); await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
await asyncExecShell( await asyncExecShell(

View File

@ -159,7 +159,7 @@ export const post: RequestHandler = async (event) => {
networks: [network], networks: [network],
volumes: [], volumes: [],
restart: 'always', restart: 'always',
...(exposePort ? { ports: [`${port}:${port}`] } : {}), ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('umami'), labels: makeLabelForServices('umami'),
deploy: { deploy: {
restart_policy: { restart_policy: {

View File

@ -78,7 +78,7 @@ export const post: RequestHandler = async (event) => {
networks: [network], networks: [network],
volumes: [config.volume, ...volumes], volumes: [config.volume, ...volumes],
restart: 'always', restart: 'always',
...(exposePort ? { ports: [`${port}:${exposePort}`] } : {}), ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
labels: makeLabelForServices('vscodeServer'), labels: makeLabelForServices('vscodeServer'),
deploy: { deploy: {
restart_policy: { restart_policy: {

View File

@ -0,0 +1,185 @@
import { dev } from '$app/env';
import { asyncExecShell, getEngine, getUserDetails } from '$lib/common';
import { decrypt, encrypt } from '$lib/crypto';
import * as db from '$lib/database';
import { ErrorHandler, generatePassword, getFreePort } from '$lib/database';
import { checkContainer, startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy';
import type { ComposeFile } from '$lib/types/composeFile';
import type { RequestHandler } from '@sveltejs/kit';
import cuid from 'cuid';
import fs from 'fs/promises';
import yaml from 'js-yaml';
export const post: RequestHandler = async (event) => {
const { status, body, teamId } = await getUserDetails(event);
if (status === 401) return { status, body };
const { id } = event.params;
const { ftpEnabled } = await event.request.json();
const publicPort = await getFreePort();
let ftpUser = cuid();
let ftpPassword = generatePassword();
const hostkeyDir = dev ? '/tmp/hostkeys' : '/app/ssl/hostkeys';
try {
const data = await db.prisma.wordpress.update({
where: { serviceId: id },
data: { ftpEnabled },
include: { service: { include: { destinationDocker: true } } }
});
const {
service: { destinationDockerId, destinationDocker },
ftpPublicPort: oldPublicPort,
ftpUser: user,
ftpPassword: savedPassword,
ftpHostKey,
ftpHostKeyPrivate
} = data;
if (user) ftpUser = user;
if (savedPassword) ftpPassword = decrypt(savedPassword);
const { stdout: password } = await asyncExecShell(
`echo ${ftpPassword} | openssl passwd -1 -stdin`
);
if (destinationDockerId) {
try {
await fs.stat(hostkeyDir);
} catch (error) {
await asyncExecShell(`mkdir -p ${hostkeyDir}`);
}
if (!ftpHostKey) {
await asyncExecShell(
`ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" -q -f ${hostkeyDir}/${id}.ed25519`
);
const { stdout: ftpHostKey } = await asyncExecShell(`cat ${hostkeyDir}/${id}.ed25519`);
await db.prisma.wordpress.update({
where: { serviceId: id },
data: { ftpHostKey: encrypt(ftpHostKey) }
});
} else {
await asyncExecShell(`echo "${decrypt(ftpHostKey)}" > ${hostkeyDir}/${id}.ed25519`);
}
if (!ftpHostKeyPrivate) {
await asyncExecShell(`ssh-keygen -t rsa -b 4096 -N "" -f ${hostkeyDir}/${id}.rsa`);
const { stdout: ftpHostKeyPrivate } = await asyncExecShell(`cat ${hostkeyDir}/${id}.rsa`);
await db.prisma.wordpress.update({
where: { serviceId: id },
data: { ftpHostKeyPrivate: encrypt(ftpHostKeyPrivate) }
});
} else {
await asyncExecShell(`echo "${decrypt(ftpHostKeyPrivate)}" > ${hostkeyDir}/${id}.rsa`);
}
const { network, engine } = destinationDocker;
const host = getEngine(engine);
if (ftpEnabled) {
await db.prisma.wordpress.update({
where: { serviceId: id },
data: {
ftpPublicPort: publicPort,
ftpUser: user ? undefined : ftpUser,
ftpPassword: savedPassword ? undefined : encrypt(ftpPassword)
}
});
try {
const isRunning = await checkContainer(engine, `${id}-ftp`);
if (isRunning) {
await asyncExecShell(
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
);
}
} catch (error) {
console.log(error);
//
}
const volumes = [
`${id}-wordpress-data:/home/${ftpUser}`,
`${
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
}/${id}.ed25519:/etc/ssh/ssh_host_ed25519_key`,
`${
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
}/${id}.rsa:/etc/ssh/ssh_host_rsa_key`,
`${
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
}/${id}.sh:/etc/sftp.d/chmod.sh`
];
const compose: ComposeFile = {
version: '3.8',
services: {
[`${id}-ftp`]: {
image: `atmoz/sftp:alpine`,
command: `'${ftpUser}:${password.replace('\n', '').replace(/\$/g, '$$$')}:e:33'`,
extra_hosts: ['host.docker.internal:host-gateway'],
container_name: `${id}-ftp`,
volumes,
networks: [network],
depends_on: [],
restart: 'always'
}
},
networks: {
[network]: {
external: true
}
},
volumes: {
[`${id}-wordpress-data`]: {
external: true,
name: `${id}-wordpress-data`
}
}
};
await fs.writeFile(
`${hostkeyDir}/${id}.sh`,
`#!/bin/bash\nchmod 600 /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_rsa_key`
);
await asyncExecShell(`chmod +x ${hostkeyDir}/${id}.sh`);
await fs.writeFile(`${hostkeyDir}/${id}-docker-compose.yml`, yaml.dump(compose));
await asyncExecShell(
`DOCKER_HOST=${host} docker compose -f ${hostkeyDir}/${id}-docker-compose.yml up -d`
);
await startTcpProxy(destinationDocker, `${id}-ftp`, publicPort, 22);
} else {
await db.prisma.wordpress.update({
where: { serviceId: id },
data: { ftpPublicPort: null }
});
try {
await asyncExecShell(
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
);
} catch (error) {
//
}
await stopTcpHttpProxy(destinationDocker, oldPublicPort);
}
}
if (ftpEnabled) {
return {
status: 201,
body: {
publicPort,
ftpUser,
ftpPassword
}
};
} else {
return {
status: 200,
body: {}
};
}
} catch (error) {
console.log(error);
return ErrorHandler(error);
} finally {
await asyncExecShell(
`rm -f ${hostkeyDir}/${id}-docker-compose.yml ${hostkeyDir}/${id}.ed25519 ${hostkeyDir}/${id}.ed25519.pub ${hostkeyDir}/${id}.rsa ${hostkeyDir}/${id}.rsa.pub ${hostkeyDir}/${id}.sh`
);
}
};

View File

@ -12,13 +12,24 @@ export const post: RequestHandler = async (event) => {
name, name,
fqdn, fqdn,
exposePort, exposePort,
wordpress: { extraConfig, mysqlDatabase } wordpress: { extraConfig, mysqlDatabase, mysqlHost, mysqlPort }
} = await event.request.json(); } = await event.request.json();
if (fqdn) fqdn = fqdn.toLowerCase(); if (fqdn) fqdn = fqdn.toLowerCase();
if (exposePort) exposePort = Number(exposePort); if (exposePort) exposePort = Number(exposePort);
if (mysqlPort) mysqlPort = Number(mysqlPort);
try { try {
await db.updateWordpress({ id, fqdn, name, extraConfig, mysqlDatabase, exposePort }); await db.updateWordpress({
id,
fqdn,
name,
extraConfig,
mysqlDatabase,
exposePort,
mysqlHost,
mysqlPort
});
return { status: 201 }; return { status: 201 };
} catch (error) { } catch (error) {
return ErrorHandler(error); return ErrorHandler(error);

View File

@ -16,170 +16,17 @@ export const post: RequestHandler = async (event) => {
const { id } = event.params; const { id } = event.params;
const { ftpEnabled } = await event.request.json(); const { ownMysql } = await event.request.json();
const publicPort = await getFreePort();
let ftpUser = cuid();
let ftpPassword = generatePassword();
const hostkeyDir = dev ? '/tmp/hostkeys' : '/app/ssl/hostkeys';
try { try {
const data = await db.prisma.wordpress.update({ await db.prisma.wordpress.update({
where: { serviceId: id }, where: { serviceId: id },
data: { ftpEnabled }, data: { ownMysql }
include: { service: { include: { destinationDocker: true } } }
}); });
const { return {
service: { destinationDockerId, destinationDocker }, status: 201
ftpPublicPort: oldPublicPort, };
ftpUser: user,
ftpPassword: savedPassword,
ftpHostKey,
ftpHostKeyPrivate
} = data;
if (user) ftpUser = user;
if (savedPassword) ftpPassword = decrypt(savedPassword);
const { stdout: password } = await asyncExecShell(
`echo ${ftpPassword} | openssl passwd -1 -stdin`
);
if (destinationDockerId) {
try {
await fs.stat(hostkeyDir);
} catch (error) {
await asyncExecShell(`mkdir -p ${hostkeyDir}`);
}
if (!ftpHostKey) {
await asyncExecShell(
`ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" -q -f ${hostkeyDir}/${id}.ed25519`
);
const { stdout: ftpHostKey } = await asyncExecShell(`cat ${hostkeyDir}/${id}.ed25519`);
await db.prisma.wordpress.update({
where: { serviceId: id },
data: { ftpHostKey: encrypt(ftpHostKey) }
});
} else {
await asyncExecShell(`echo "${decrypt(ftpHostKey)}" > ${hostkeyDir}/${id}.ed25519`);
}
if (!ftpHostKeyPrivate) {
await asyncExecShell(`ssh-keygen -t rsa -b 4096 -N "" -f ${hostkeyDir}/${id}.rsa`);
const { stdout: ftpHostKeyPrivate } = await asyncExecShell(`cat ${hostkeyDir}/${id}.rsa`);
await db.prisma.wordpress.update({
where: { serviceId: id },
data: { ftpHostKeyPrivate: encrypt(ftpHostKeyPrivate) }
});
} else {
await asyncExecShell(`echo "${decrypt(ftpHostKeyPrivate)}" > ${hostkeyDir}/${id}.rsa`);
}
const { network, engine } = destinationDocker;
const host = getEngine(engine);
if (ftpEnabled) {
await db.prisma.wordpress.update({
where: { serviceId: id },
data: {
ftpPublicPort: publicPort,
ftpUser: user ? undefined : ftpUser,
ftpPassword: savedPassword ? undefined : encrypt(ftpPassword)
}
});
try {
const isRunning = await checkContainer(engine, `${id}-ftp`);
if (isRunning) {
await asyncExecShell(
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
);
}
} catch (error) {
console.log(error);
//
}
const volumes = [
`${id}-wordpress-data:/home/${ftpUser}`,
`${
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
}/${id}.ed25519:/etc/ssh/ssh_host_ed25519_key`,
`${
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
}/${id}.rsa:/etc/ssh/ssh_host_rsa_key`,
`${
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
}/${id}.sh:/etc/sftp.d/chmod.sh`
];
const compose: ComposeFile = {
version: '3.8',
services: {
[`${id}-ftp`]: {
image: `atmoz/sftp:alpine`,
command: `'${ftpUser}:${password.replace('\n', '').replace(/\$/g, '$$$')}:e:33'`,
extra_hosts: ['host.docker.internal:host-gateway'],
container_name: `${id}-ftp`,
volumes,
networks: [network],
depends_on: [],
restart: 'always'
}
},
networks: {
[network]: {
external: true
}
},
volumes: {
[`${id}-wordpress-data`]: {
external: true,
name: `${id}-wordpress-data`
}
}
};
await fs.writeFile(
`${hostkeyDir}/${id}.sh`,
`#!/bin/bash\nchmod 600 /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_rsa_key`
);
await asyncExecShell(`chmod +x ${hostkeyDir}/${id}.sh`);
await fs.writeFile(`${hostkeyDir}/${id}-docker-compose.yml`, yaml.dump(compose));
await asyncExecShell(
`DOCKER_HOST=${host} docker compose -f ${hostkeyDir}/${id}-docker-compose.yml up -d`
);
await startTcpProxy(destinationDocker, `${id}-ftp`, publicPort, 22);
} else {
await db.prisma.wordpress.update({
where: { serviceId: id },
data: { ftpPublicPort: null }
});
try {
await asyncExecShell(
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
);
} catch (error) {
//
}
await stopTcpHttpProxy(destinationDocker, oldPublicPort);
}
}
if (ftpEnabled) {
return {
status: 201,
body: {
publicPort,
ftpUser,
ftpPassword
}
};
} else {
return {
status: 200,
body: {}
};
}
} catch (error) { } catch (error) {
console.log(error); console.log(error);
return ErrorHandler(error); return ErrorHandler(error);
} finally {
await asyncExecShell(
`rm -f ${hostkeyDir}/${id}-docker-compose.yml ${hostkeyDir}/${id}.ed25519 ${hostkeyDir}/${id}.ed25519.pub ${hostkeyDir}/${id}.rsa ${hostkeyDir}/${id}.rsa.pub ${hostkeyDir}/${id}.sh`
);
} }
}; };

View File

@ -26,11 +26,14 @@ export const post: RequestHandler = async (event) => {
exposePort, exposePort,
wordpress: { wordpress: {
mysqlDatabase, mysqlDatabase,
mysqlHost,
mysqlPort,
mysqlUser, mysqlUser,
mysqlPassword, mysqlPassword,
extraConfig, extraConfig,
mysqlRootUser, mysqlRootUser,
mysqlRootUserPassword mysqlRootUserPassword,
ownMysql
} }
} = service; } = service;
@ -45,7 +48,7 @@ export const post: RequestHandler = async (event) => {
image: `${image}:${version}`, image: `${image}:${version}`,
volume: `${id}-wordpress-data:/var/www/html`, volume: `${id}-wordpress-data:/var/www/html`,
environmentVariables: { environmentVariables: {
WORDPRESS_DB_HOST: `${id}-mysql`, WORDPRESS_DB_HOST: ownMysql ? `${mysqlHost}:${mysqlPort}` : `${id}-mysql`,
WORDPRESS_DB_USER: mysqlUser, WORDPRESS_DB_USER: mysqlUser,
WORDPRESS_DB_PASSWORD: mysqlPassword, WORDPRESS_DB_PASSWORD: mysqlPassword,
WORDPRESS_DB_NAME: mysqlDatabase, WORDPRESS_DB_NAME: mysqlDatabase,
@ -69,7 +72,7 @@ export const post: RequestHandler = async (event) => {
config.wordpress.environmentVariables[secret.name] = secret.value; config.wordpress.environmentVariables[secret.name] = secret.value;
}); });
} }
const composeFile: ComposeFile = { let composeFile: ComposeFile = {
version: '3.8', version: '3.8',
services: { services: {
[id]: { [id]: {
@ -79,8 +82,7 @@ export const post: RequestHandler = async (event) => {
volumes: [config.wordpress.volume], volumes: [config.wordpress.volume],
networks: [network], networks: [network],
restart: 'always', restart: 'always',
...(exposePort ? { ports: [`${port}:${port}`] } : {}), ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
depends_on: [`${id}-mysql`],
labels: makeLabelForServices('wordpress'), labels: makeLabelForServices('wordpress'),
deploy: { deploy: {
restart_policy: { restart_policy: {
@ -90,22 +92,6 @@ export const post: RequestHandler = async (event) => {
window: '120s' window: '120s'
} }
} }
},
[`${id}-mysql`]: {
container_name: `${id}-mysql`,
image: config.mysql.image,
volumes: [config.mysql.volume],
environment: config.mysql.environmentVariables,
networks: [network],
restart: 'always',
deploy: {
restart_policy: {
condition: 'on-failure',
delay: '5s',
max_attempts: 3,
window: '120s'
}
}
} }
}, },
networks: { networks: {
@ -116,12 +102,32 @@ export const post: RequestHandler = async (event) => {
volumes: { volumes: {
[config.wordpress.volume.split(':')[0]]: { [config.wordpress.volume.split(':')[0]]: {
name: config.wordpress.volume.split(':')[0] name: config.wordpress.volume.split(':')[0]
},
[config.mysql.volume.split(':')[0]]: {
name: config.mysql.volume.split(':')[0]
} }
} }
}; };
if (!ownMysql) {
composeFile.services[id].depends_on = [`${id}-mysql`];
composeFile.services[`${id}-mysql`] = {
container_name: `${id}-mysql`,
image: config.mysql.image,
volumes: [config.mysql.volume],
environment: config.mysql.environmentVariables,
networks: [network],
restart: 'always',
deploy: {
restart_policy: {
condition: 'on-failure',
delay: '5s',
max_attempts: 3,
window: '120s'
}
}
};
composeFile.volumes[config.mysql.volume.split(':')[0]] = {
name: config.mysql.volume.split(':')[0]
};
}
const composeFileDestination = `${workdir}/docker-compose.yaml`; const composeFileDestination = `${workdir}/docker-compose.yaml`;
await fs.writeFile(composeFileDestination, yaml.dump(composeFile)); await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
try { try {

View File

@ -55,7 +55,7 @@
</div> </div>
</div> </div>
<div class="flex flex-col flex-wrap justify-center"> <div class="flex justify-center">
{#if !services || ownServices.length === 0} {#if !services || ownServices.length === 0}
<div class="flex-col"> <div class="flex-col">
<div class="text-center text-xl font-bold">{$t('service.no_service')}</div> <div class="text-center text-xl font-bold">{$t('service.no_service')}</div>

View File

@ -68,10 +68,48 @@
} }
</script> </script>
<div class="flex space-x-1 p-6 px-6 text-2xl font-bold"> <div class="flex h-20 items-center space-x-2 p-5 px-6 font-bold">
<div class="tracking-tight">{$t('application.git_source')}</div> <div class="-mb-5 flex-col">
<span class="arrow-right-applications px-1 text-orange-500">></span> <div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
<span class="pr-2">{source.name}</span> Configuration
</div>
<span class="text-xs">{source.name}</span>
</div>
{#if source?.type === 'gitlab'}
<svg viewBox="0 0 128 128" class="w-8">
<path
fill="#FC6D26"
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
fill="#FC6D26"
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
/><path
fill="#FCA326"
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
/><path
fill="#E24329"
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
fill="#FCA326"
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
/><path
fill="#E24329"
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
/>
</svg>
{:else if source?.type === 'github'}
<svg viewBox="0 0 128 128" class="w-8">
<g fill="#ffffff"
><path
fill-rule="evenodd"
clip-rule="evenodd"
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
/><path
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
/></g
>
</svg>
{/if}
</div> </div>
<div class="flex flex-col justify-center"> <div class="flex flex-col justify-center">

View File

@ -62,7 +62,7 @@
</button> </button>
{/if} {/if}
</div> </div>
<div class="flex flex-col flex-wrap justify-center"> <div class="flex justify-center">
{#if !sources || ownSources.length === 0} {#if !sources || ownSources.length === 0}
<div class="flex-col"> <div class="flex-col">
<div class="text-center text-xl font-bold">{$t('source.no_git_sources_found')}</div> <div class="text-center text-xl font-bold">{$t('source.no_git_sources_found')}</div>
@ -74,11 +74,48 @@
{#each ownSources as source} {#each ownSources as source}
<a href="/sources/{source.id}" class="w-96 p-2 no-underline"> <a href="/sources/{source.id}" class="w-96 p-2 no-underline">
<div <div
class="box-selection group hover:bg-orange-600" class="box-selection group relative hover:bg-orange-600"
class:border-red-500={source.gitlabApp && !source.gitlabAppId} class:border-red-500={source.gitlabApp && !source.gitlabAppId}
class:border-0={source.gitlabApp && !source.gitlabAppId} class:border-0={source.gitlabApp && !source.gitlabAppId}
class:border-l-4={source.gitlabApp && !source.gitlabAppId} class:border-l-4={source.gitlabApp && !source.gitlabAppId}
> >
<div class="absolute top-0 left-0 -m-5 h-10 w-10">
{#if source?.type === 'gitlab'}
<svg viewBox="0 0 128 128">
<path
fill="#FC6D26"
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
fill="#FC6D26"
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
/><path
fill="#FCA326"
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
/><path
fill="#E24329"
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
fill="#FCA326"
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
/><path
fill="#E24329"
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
/>
</svg>
{:else if source?.type === 'github'}
<svg viewBox="0 0 128 128">
<g fill="#ffffff"
><path
fill-rule="evenodd"
clip-rule="evenodd"
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
/><path
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
/></g
>
</svg>
{/if}
</div>
<div class="truncate text-center text-xl font-bold">{source.name}</div> <div class="truncate text-center text-xl font-bold">{source.name}</div>
{#if $session.teamId === '0' && otherSources.length > 0} {#if $session.teamId === '0' && otherSources.length > 0}
<div class="truncate text-center">{source.teams[0].name}</div> <div class="truncate text-center">{source.teams[0].name}</div>

View File

@ -107,7 +107,7 @@ a {
@apply mr-4 text-base tracking-tight md:text-2xl; @apply mr-4 text-base tracking-tight md:text-2xl;
} }
.nav-main { .nav-main {
@apply fixed top-0 left-0 min-h-screen w-16 min-w-[4rem] border-r border-stone-800 bg-coolgray-200; @apply fixed top-0 left-0 min-h-screen w-16 min-w-[4rem] overflow-auto border-r border-stone-800 bg-coolgray-200 scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200 xl:overflow-visible;
} }
.nav-side { .nav-side {
@ -356,7 +356,7 @@ a {
} }
.box-selection { .box-selection {
@apply min-w-[16rem] max-w-[24rem] justify-center rounded-lg border-transparent bg-coolgray-200 p-6 shadow-lg transition duration-150 hover:scale-105 hover:border-transparent hover:bg-coolgray-400; @apply min-w-[16rem] max-w-[24rem] justify-center rounded border-transparent bg-coolgray-200 p-6 hover:border-transparent hover:bg-coolgray-400;
} }
._toastBar { ._toastBar {