Merge branch 'next' into tool-tip
This commit is contained in:
commit
a46773e6d8
@ -8,7 +8,6 @@ package
|
||||
.env.*
|
||||
!.env.example
|
||||
dist
|
||||
client
|
||||
apps/api/db/*.db
|
||||
local-serve
|
||||
apps/api/db/migration.db-journal
|
||||
|
10
.github/ISSUE_TEMPLATE/--bug-report.yaml
vendored
10
.github/ISSUE_TEMPLATE/--bug-report.yaml
vendored
@ -9,13 +9,21 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please fill the form in English
|
||||
Thanks for taking the time to fill out this bug report! Please fill the form in English.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: input
|
||||
id: repository
|
||||
attributes:
|
||||
label: Example public repository
|
||||
description: "An example public git repository to reproduce the issue easily (if applicable)."
|
||||
placeholder: "ex: https://github.com/coollabsio/coolify"
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
|
14
.github/workflows/pocketbase-release.yml
vendored
14
.github/workflows/pocketbase-release.yml
vendored
@ -3,10 +3,8 @@ name: pocketbase-release
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "others/pocketbase"
|
||||
- "others/pocketbase/*"
|
||||
- ".github/workflows/pocketbase-release.yml"
|
||||
branches:
|
||||
- next
|
||||
|
||||
jobs:
|
||||
arm64:
|
||||
@ -29,7 +27,7 @@ jobs:
|
||||
context: others/pocketbase/
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
tags: coollabsio/pocketbase:0.8.0-arm64
|
||||
tags: coollabsio/pocketbase:0.10.2-arm64
|
||||
amd64:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -50,7 +48,7 @@ jobs:
|
||||
context: others/pocketbase/
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: coollabsio/pocketbase:0.8.0-amd64
|
||||
tags: coollabsio/pocketbase:0.10.2-amd64
|
||||
aarch64:
|
||||
runs-on: [self-hosted, arm64]
|
||||
steps:
|
||||
@ -71,7 +69,7 @@ jobs:
|
||||
context: others/pocketbase/
|
||||
platforms: linux/aarch64
|
||||
push: true
|
||||
tags: coollabsio/pocketbase:0.8.0-aarch64
|
||||
tags: coollabsio/pocketbase:0.10.2-aarch64
|
||||
merge-manifest:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [amd64, arm64, aarch64]
|
||||
@ -89,5 +87,5 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Create & publish manifest
|
||||
run: |
|
||||
docker manifest create coollabsio/pocketbase:0.8.0 --amend coollabsio/pocketbase:0.8.0-amd64 --amend coollabsio/pocketbase:0.8.0-arm64 --amend coollabsio/pocketbase:0.8.0-aarch64
|
||||
docker manifest push coollabsio/pocketbase:0.8.0
|
||||
docker manifest create coollabsio/pocketbase:0.10.2 --amend coollabsio/pocketbase:0.10.2-amd64 --amend coollabsio/pocketbase:0.10.2-arm64 --amend coollabsio/pocketbase:0.10.2-aarch64
|
||||
docker manifest push coollabsio/pocketbase:0.10.2
|
||||
|
2
.github/workflows/production-release.yml
vendored
2
.github/workflows/production-release.yml
vendored
@ -104,7 +104,9 @@ jobs:
|
||||
- name: Create & publish manifest
|
||||
run: |
|
||||
docker manifest create coollabsio/coolify:${{steps.package-version.outputs.current-version}} --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64
|
||||
docker manifest create coollabsio/coolify:latest --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64
|
||||
docker manifest push coollabsio/coolify:${{steps.package-version.outputs.current-version}}
|
||||
docker manifest push coollabsio/coolify:latest
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
if: always()
|
||||
with:
|
||||
|
13
.gitignore
vendored
13
.gitignore
vendored
@ -1,20 +1,25 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
.pnpm-store
|
||||
build
|
||||
/apps/ui/build
|
||||
/build
|
||||
.svelte-kit
|
||||
package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
dist
|
||||
client
|
||||
apps/api/db/*.db
|
||||
apps/api/db/migration.db-journal
|
||||
apps/api/core*
|
||||
apps/server/build
|
||||
apps/backup/backups/*
|
||||
!apps/backup/backups/.gitkeep
|
||||
logs
|
||||
/logs
|
||||
others/certificates
|
||||
backups/*
|
||||
!backups/.gitkeep
|
||||
!backups/.gitkeep
|
||||
|
||||
# Trpc
|
||||
apps/server/db/*.db
|
||||
apps/server/db/*.db-journal
|
BIN
apps/api/db/dev.db.bak
Normal file
BIN
apps/api/db/dev.db.bak
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: '0.8.0'
|
||||
defaultVersion: "0.10.2"
|
||||
documentation: https://pocketbase.io/docs/
|
||||
type: pocketbase
|
||||
name: Pocketbase
|
||||
@ -12,7 +12,7 @@
|
||||
ports:
|
||||
- "8080"
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 1.5.0-rc.0
|
||||
defaultVersion: v1.5.1
|
||||
documentation: https://plausible.io/doc/
|
||||
type: plausibleanalytics-arm
|
||||
name: Plausible Analytics (ARM)
|
||||
@ -313,7 +313,7 @@
|
||||
defaultValue: keycloak
|
||||
description: ""
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: v3.6
|
||||
defaultVersion: v3.7
|
||||
documentation: https://github.com/freyacodes/Lavalink
|
||||
description: Standalone audio sending node based on Lavaplayer.
|
||||
type: lavalink
|
||||
@ -375,7 +375,7 @@
|
||||
defaultValue: $$generate_password
|
||||
required: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: v1.8.6
|
||||
defaultVersion: v1.8.9
|
||||
documentation: https://docs.appsmith.com/getting-started/setup/instance-configuration/
|
||||
type: appsmith
|
||||
name: Appsmith
|
||||
@ -408,7 +408,7 @@
|
||||
defaultValue: "true"
|
||||
description: ""
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 0.56.2
|
||||
defaultVersion: 0.57.4
|
||||
documentation: https://hub.docker.com/r/zadam/trilium
|
||||
description: "A hierarchical note taking application with focus on building large personal knowledge bases."
|
||||
labels:
|
||||
@ -428,7 +428,7 @@
|
||||
- "8080"
|
||||
variables: []
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 1.9.2
|
||||
defaultVersion: 1.18.5
|
||||
documentation: https://hub.docker.com/r/louislam/uptime-kuma
|
||||
description: A free & fancy self-hosted monitoring tool.
|
||||
labels:
|
||||
@ -478,7 +478,7 @@
|
||||
- "80"
|
||||
variables: []
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 9.2.3
|
||||
defaultVersion: 9.3.1
|
||||
documentation: https://hub.docker.com/r/grafana/grafana
|
||||
type: grafana
|
||||
name: Grafana
|
||||
@ -499,7 +499,7 @@
|
||||
- "3000"
|
||||
variables: []
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 1.0.3
|
||||
defaultVersion: 1.1.2
|
||||
documentation: https://appwrite.io/docs
|
||||
type: appwrite
|
||||
name: Appwrite
|
||||
@ -646,6 +646,7 @@
|
||||
- "$$id-functions:/storage/functions"
|
||||
- "$$id-builds:/storage/builds"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
- "/tmp:/tmp:rw"
|
||||
entrypoint: executor
|
||||
"$$id-influxdb":
|
||||
image: appwrite/influxdb:1.5.0
|
||||
@ -1668,7 +1669,7 @@
|
||||
defaultValue: weblate
|
||||
description: ""
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 2022.10.14-1a5b0965
|
||||
defaultVersion: 2022.12.12-966e9c3c
|
||||
documentation: https://docs.searxng.org/
|
||||
type: searxng
|
||||
name: SearXNG
|
||||
@ -1741,7 +1742,7 @@
|
||||
defaultValue: $$generate_password
|
||||
description: ""
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: v2.0.6
|
||||
defaultVersion: v3.0.0
|
||||
documentation: https://glitchtip.com/documentation
|
||||
type: glitchtip
|
||||
name: GlitchTip
|
||||
@ -1963,7 +1964,7 @@
|
||||
defaultValue: glitchtip
|
||||
description: ""
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: v2.13.0
|
||||
defaultVersion: v2.16.0
|
||||
documentation: https://hasura.io/docs/latest/index/
|
||||
type: hasura
|
||||
name: Hasura
|
||||
@ -2030,7 +2031,7 @@
|
||||
defaultValue: hasura
|
||||
description: ""
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: postgresql-v1.38.0
|
||||
defaultVersion: postgresql-v1.39.5
|
||||
documentation: https://umami.is/docs/getting-started
|
||||
type: umami-postgresql
|
||||
name: Umami
|
||||
@ -2074,10 +2075,10 @@
|
||||
"is_admin" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
|
||||
PRIMARY KEY ("user_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "event" (
|
||||
"event_id" SERIAL NOT NULL,
|
||||
@ -2087,10 +2088,10 @@
|
||||
"url" VARCHAR(500) NOT NULL,
|
||||
"event_type" VARCHAR(50) NOT NULL,
|
||||
"event_value" VARCHAR(50) NOT NULL,
|
||||
|
||||
|
||||
PRIMARY KEY ("event_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "pageview" (
|
||||
"view_id" SERIAL NOT NULL,
|
||||
@ -2099,10 +2100,10 @@
|
||||
"created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
"url" VARCHAR(500) NOT NULL,
|
||||
"referrer" VARCHAR(500),
|
||||
|
||||
|
||||
PRIMARY KEY ("view_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "session" (
|
||||
"session_id" SERIAL NOT NULL,
|
||||
@ -2116,10 +2117,10 @@
|
||||
"screen" VARCHAR(11),
|
||||
"language" VARCHAR(35),
|
||||
"country" CHAR(2),
|
||||
|
||||
|
||||
PRIMARY KEY ("session_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "website" (
|
||||
"website_id" SERIAL NOT NULL,
|
||||
@ -2129,73 +2130,73 @@
|
||||
"domain" VARCHAR(500),
|
||||
"share_id" VARCHAR(64),
|
||||
"created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
|
||||
PRIMARY KEY ("website_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "account.username_unique" ON "account"("username");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "event_created_at_idx" ON "event"("created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "event_session_id_idx" ON "event"("session_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "event_website_id_idx" ON "event"("website_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_created_at_idx" ON "pageview"("created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_session_id_idx" ON "pageview"("session_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_website_id_created_at_idx" ON "pageview"("website_id", "created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_website_id_idx" ON "pageview"("website_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_website_id_session_id_created_at_idx" ON "pageview"("website_id", "session_id", "created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "session.session_uuid_unique" ON "session"("session_uuid");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "session_created_at_idx" ON "session"("created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "session_website_id_idx" ON "session"("website_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "website.website_uuid_unique" ON "website"("website_uuid");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "website.share_id_unique" ON "website"("share_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "website_user_id_idx" ON "website"("user_id");
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "event" ADD FOREIGN KEY ("session_id") REFERENCES "session"("session_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "event" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "pageview" ADD FOREIGN KEY ("session_id") REFERENCES "session"("session_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "pageview" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "session" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "website" ADD FOREIGN KEY ("user_id") REFERENCES "account"("user_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);
|
||||
variables:
|
||||
- id: $$secret_database_url
|
||||
@ -2237,7 +2238,7 @@
|
||||
showOnConfiguration: true
|
||||
- templateVersion: 1.0.0
|
||||
ignore: true
|
||||
defaultVersion: postgresql-v1.38.0
|
||||
defaultVersion: postgresql-v1.39.5
|
||||
documentation: https://umami.is/docs/getting-started
|
||||
type: umami
|
||||
name: Umami
|
||||
@ -2281,10 +2282,10 @@
|
||||
"is_admin" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
|
||||
PRIMARY KEY ("user_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "event" (
|
||||
"event_id" SERIAL NOT NULL,
|
||||
@ -2294,10 +2295,10 @@
|
||||
"url" VARCHAR(500) NOT NULL,
|
||||
"event_type" VARCHAR(50) NOT NULL,
|
||||
"event_value" VARCHAR(50) NOT NULL,
|
||||
|
||||
|
||||
PRIMARY KEY ("event_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "pageview" (
|
||||
"view_id" SERIAL NOT NULL,
|
||||
@ -2306,10 +2307,10 @@
|
||||
"created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
"url" VARCHAR(500) NOT NULL,
|
||||
"referrer" VARCHAR(500),
|
||||
|
||||
|
||||
PRIMARY KEY ("view_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "session" (
|
||||
"session_id" SERIAL NOT NULL,
|
||||
@ -2323,10 +2324,10 @@
|
||||
"screen" VARCHAR(11),
|
||||
"language" VARCHAR(35),
|
||||
"country" CHAR(2),
|
||||
|
||||
|
||||
PRIMARY KEY ("session_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "website" (
|
||||
"website_id" SERIAL NOT NULL,
|
||||
@ -2336,73 +2337,73 @@
|
||||
"domain" VARCHAR(500),
|
||||
"share_id" VARCHAR(64),
|
||||
"created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
|
||||
PRIMARY KEY ("website_id")
|
||||
);
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "account.username_unique" ON "account"("username");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "event_created_at_idx" ON "event"("created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "event_session_id_idx" ON "event"("session_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "event_website_id_idx" ON "event"("website_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_created_at_idx" ON "pageview"("created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_session_id_idx" ON "pageview"("session_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_website_id_created_at_idx" ON "pageview"("website_id", "created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_website_id_idx" ON "pageview"("website_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pageview_website_id_session_id_created_at_idx" ON "pageview"("website_id", "session_id", "created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "session.session_uuid_unique" ON "session"("session_uuid");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "session_created_at_idx" ON "session"("created_at");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "session_website_id_idx" ON "session"("website_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "website.website_uuid_unique" ON "website"("website_uuid");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "website.share_id_unique" ON "website"("share_id");
|
||||
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "website_user_id_idx" ON "website"("user_id");
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "event" ADD FOREIGN KEY ("session_id") REFERENCES "session"("session_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "event" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "pageview" ADD FOREIGN KEY ("session_id") REFERENCES "session"("session_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "pageview" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "session" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "website" ADD FOREIGN KEY ("user_id") REFERENCES "account"("user_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);
|
||||
variables:
|
||||
- id: $$secret_database_url
|
||||
@ -2443,7 +2444,7 @@
|
||||
description: ""
|
||||
showOnConfiguration: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: v0.29.1
|
||||
defaultVersion: v0.30.1
|
||||
documentation: https://docs.meilisearch.com/learn/getting_started/quick_start.html
|
||||
type: meilisearch
|
||||
name: MeiliSearch
|
||||
@ -2591,7 +2592,7 @@
|
||||
defaultValue: $$generate_password
|
||||
description: ""
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: "5.22"
|
||||
defaultVersion: "5.25.3"
|
||||
documentation: https://docs.ghost.org
|
||||
type: ghost-only
|
||||
name: Ghost
|
||||
@ -2655,7 +2656,7 @@
|
||||
placeholder: "ghost_db"
|
||||
required: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: "5.22"
|
||||
defaultVersion: "5.25.3"
|
||||
documentation: https://docs.ghost.org
|
||||
type: ghost-mysql
|
||||
name: Ghost
|
||||
@ -2896,7 +2897,7 @@
|
||||
define('WP_DEBUG_DISPLAY', false);
|
||||
@ini_set('display_errors', 0);
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 4.7.1
|
||||
defaultVersion: 4.9.0
|
||||
documentation: https://coder.com/docs/coder-oss/latest
|
||||
type: vscodeserver
|
||||
name: VSCode Server
|
||||
@ -2927,7 +2928,7 @@
|
||||
description: ""
|
||||
showOnConfiguration: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: RELEASE.2022-10-15T19-57-03Z
|
||||
defaultVersion: RELEASE.2022-12-12T19-27-27Z
|
||||
documentation: https://min.io/docs/minio
|
||||
type: minio
|
||||
name: MinIO
|
||||
@ -3105,7 +3106,7 @@
|
||||
defaultValue: $$generate_username
|
||||
description: ""
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 0.198.1
|
||||
defaultVersion: 0.207.0
|
||||
documentation: https://docs.n8n.io
|
||||
type: n8n
|
||||
name: n8n.io
|
||||
@ -3282,7 +3283,7 @@
|
||||
defaultValue: plausible.js
|
||||
description: This is the default script name.
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 0.98.1
|
||||
defaultVersion: 0.99.1
|
||||
documentation: https://docs.nocodb.com
|
||||
type: nocodb
|
||||
name: NocoDB
|
||||
|
@ -1,85 +1,85 @@
|
||||
{
|
||||
"name": "api",
|
||||
"description": "Coolify's Fastify API",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"db:generate": "prisma generate",
|
||||
"db:push": "prisma db push && prisma generate",
|
||||
"db:seed": "prisma db seed",
|
||||
"db:studio": "prisma studio",
|
||||
"db:migrate": "COOLIFY_DATABASE_URL=file:../db/migration.db prisma migrate dev --skip-seed --name",
|
||||
"dev": "nodemon",
|
||||
"build": "rimraf build && esbuild `find src \\( -name '*.ts' \\)| grep -v client/` --platform=node --outdir=build --format=cjs",
|
||||
"format": "prettier --write 'src/**/*.{js,ts,json,md}'",
|
||||
"lint": "prettier --check 'src/**/*.{js,ts,json,md}' && eslint --ignore-path .eslintignore .",
|
||||
"start": "NODE_ENV=production pnpm prisma migrate deploy && pnpm prisma generate && pnpm prisma db seed && node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@breejs/ts-worker": "2.0.0",
|
||||
"@fastify/autoload": "5.5.0",
|
||||
"@fastify/cookie": "8.3.0",
|
||||
"@fastify/cors": "8.2.0",
|
||||
"@fastify/env": "4.1.0",
|
||||
"@fastify/jwt": "6.3.3",
|
||||
"@fastify/multipart": "7.3.0",
|
||||
"@fastify/static": "6.5.1",
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@ladjs/graceful": "3.0.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"@sentry/node": "7.21.1",
|
||||
"@sentry/tracing": "7.21.1",
|
||||
"axe": "11.0.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bree": "9.1.2",
|
||||
"cabin": "11.0.1",
|
||||
"compare-versions": "5.0.1",
|
||||
"csv-parse": "5.3.2",
|
||||
"csvtojson": "2.0.10",
|
||||
"cuid": "2.1.8",
|
||||
"dayjs": "1.11.6",
|
||||
"dockerode": "3.3.4",
|
||||
"dotenv-extended": "2.9.0",
|
||||
"execa": "6.1.0",
|
||||
"fastify": "4.10.2",
|
||||
"fastify-plugin": "4.3.0",
|
||||
"fastify-socket.io": "4.0.0",
|
||||
"generate-password": "1.7.0",
|
||||
"got": "12.5.3",
|
||||
"is-ip": "5.0.0",
|
||||
"is-port-reachable": "4.0.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"minimist": "^1.2.7",
|
||||
"node-forge": "1.3.1",
|
||||
"node-os-utils": "1.3.7",
|
||||
"p-all": "4.0.0",
|
||||
"p-throttle": "5.0.0",
|
||||
"prisma": "4.6.1",
|
||||
"public-ip": "6.0.1",
|
||||
"pump": "3.0.0",
|
||||
"shell-quote": "^1.7.4",
|
||||
"socket.io": "4.5.3",
|
||||
"ssh-config": "4.1.6",
|
||||
"strip-ansi": "7.0.1",
|
||||
"unique-names-generator": "4.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.11.9",
|
||||
"@types/node-os-utils": "1.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.44.0",
|
||||
"@typescript-eslint/parser": "5.44.0",
|
||||
"esbuild": "0.15.15",
|
||||
"eslint": "8.28.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"nodemon": "2.0.20",
|
||||
"prettier": "2.7.1",
|
||||
"rimraf": "3.0.2",
|
||||
"tsconfig-paths": "4.1.0",
|
||||
"types-fastify-socket.io": "0.0.1",
|
||||
"typescript": "4.9.3"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "node prisma/seed.js"
|
||||
}
|
||||
}
|
||||
"name": "api",
|
||||
"description": "Coolify's Fastify API",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"db:generate": "prisma generate",
|
||||
"db:push": "prisma db push && prisma generate",
|
||||
"db:seed": "prisma db seed",
|
||||
"db:studio": "prisma studio",
|
||||
"db:migrate": "COOLIFY_DATABASE_URL=file:../db/migration.db prisma migrate dev --skip-seed --name",
|
||||
"dev": "nodemon",
|
||||
"build": "rimraf build && esbuild `find src \\( -name '*.ts' \\)| grep -v client/` --platform=node --outdir=build --format=cjs",
|
||||
"format": "prettier --write 'src/**/*.{js,ts,json,md}'",
|
||||
"lint": "prettier --check 'src/**/*.{js,ts,json,md}' && eslint --ignore-path .eslintignore .",
|
||||
"start": "NODE_ENV=production pnpm prisma migrate deploy && pnpm prisma generate && pnpm prisma db seed && node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@breejs/ts-worker": "2.0.0",
|
||||
"@fastify/autoload": "5.5.0",
|
||||
"@fastify/cookie": "8.3.0",
|
||||
"@fastify/cors": "8.2.0",
|
||||
"@fastify/env": "4.1.0",
|
||||
"@fastify/jwt": "6.3.3",
|
||||
"@fastify/multipart": "7.3.0",
|
||||
"@fastify/static": "6.5.1",
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@ladjs/graceful": "3.0.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"@sentry/node": "7.21.1",
|
||||
"@sentry/tracing": "7.21.1",
|
||||
"axe": "11.0.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bree": "9.1.2",
|
||||
"cabin": "11.0.1",
|
||||
"compare-versions": "5.0.1",
|
||||
"csv-parse": "5.3.2",
|
||||
"csvtojson": "2.0.10",
|
||||
"cuid": "2.1.8",
|
||||
"dayjs": "1.11.6",
|
||||
"dockerode": "3.3.4",
|
||||
"dotenv-extended": "2.9.0",
|
||||
"execa": "6.1.0",
|
||||
"fastify": "4.10.2",
|
||||
"fastify-plugin": "4.3.0",
|
||||
"fastify-socket.io": "4.0.0",
|
||||
"generate-password": "1.7.0",
|
||||
"got": "12.5.3",
|
||||
"is-ip": "5.0.0",
|
||||
"is-port-reachable": "4.0.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"minimist": "^1.2.7",
|
||||
"node-forge": "1.3.1",
|
||||
"node-os-utils": "1.3.7",
|
||||
"p-all": "4.0.0",
|
||||
"p-throttle": "5.0.0",
|
||||
"prisma": "4.6.1",
|
||||
"public-ip": "6.0.1",
|
||||
"pump": "3.0.0",
|
||||
"shell-quote": "^1.7.4",
|
||||
"socket.io": "4.5.3",
|
||||
"ssh-config": "4.1.6",
|
||||
"strip-ansi": "7.0.1",
|
||||
"unique-names-generator": "4.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.11.9",
|
||||
"@types/node-os-utils": "1.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.44.0",
|
||||
"@typescript-eslint/parser": "5.44.0",
|
||||
"esbuild": "0.15.15",
|
||||
"eslint": "8.28.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"nodemon": "2.0.20",
|
||||
"prettier": "2.7.1",
|
||||
"rimraf": "3.0.2",
|
||||
"tsconfig-paths": "4.1.0",
|
||||
"types-fastify-socket.io": "0.0.1",
|
||||
"typescript": "4.9.3"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "node prisma/seed.js"
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ CREATE TABLE "new_Setting" (
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "numberOfDockerImagesKeptLocally", "proxyDefaultRedirect", "sentryDSN", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "numberOfDockerImagesKeptLocally", "proxyDefaultRedirect", "sentryDSN", "updatedAt" FROM "Setting";
|
||||
INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "numberOfDockerImagesKeptLocally", "proxyDefaultRedirect", "sentryDSN", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", 3, "proxyDefaultRedirect", "sentryDSN", "updatedAt" FROM "Setting";
|
||||
DROP TABLE "Setting";
|
||||
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||
|
@ -0,0 +1,27 @@
|
||||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_GitSource" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT NOT NULL,
|
||||
"forPublic" BOOLEAN NOT NULL DEFAULT false,
|
||||
"type" TEXT,
|
||||
"apiUrl" TEXT,
|
||||
"htmlUrl" TEXT,
|
||||
"customPort" INTEGER NOT NULL DEFAULT 22,
|
||||
"customUser" TEXT NOT NULL DEFAULT 'git',
|
||||
"organization" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
"githubAppId" TEXT,
|
||||
"gitlabAppId" TEXT,
|
||||
"isSystemWide" BOOLEAN NOT NULL DEFAULT false,
|
||||
CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "isSystemWide", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "isSystemWide", "name", "organization", "type", "updatedAt" FROM "GitSource";
|
||||
DROP TABLE "GitSource";
|
||||
ALTER TABLE "new_GitSource" RENAME TO "GitSource";
|
||||
CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId");
|
||||
CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId");
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
@ -325,6 +325,7 @@ model GitSource {
|
||||
apiUrl String?
|
||||
htmlUrl String?
|
||||
customPort Int @default(22)
|
||||
customUser String @default("git")
|
||||
organization String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
@ -6,14 +6,27 @@ import cookie from '@fastify/cookie';
|
||||
import multipart from '@fastify/multipart';
|
||||
import path, { join } from 'path';
|
||||
import autoLoad from '@fastify/autoload';
|
||||
import socketIO from 'fastify-socket.io'
|
||||
import socketIOServer from './realtime'
|
||||
import socketIO from 'fastify-socket.io';
|
||||
import socketIOServer from './realtime';
|
||||
|
||||
import { cleanupDockerStorage, createRemoteEngineConfiguration, decrypt, executeCommand, generateDatabaseConfiguration, isDev, listSettings, prisma, sentryDSN, startTraefikProxy, startTraefikTCPProxy, version } from './lib/common';
|
||||
import {
|
||||
cleanupDockerStorage,
|
||||
createRemoteEngineConfiguration,
|
||||
decrypt,
|
||||
executeCommand,
|
||||
generateDatabaseConfiguration,
|
||||
isDev,
|
||||
listSettings,
|
||||
prisma,
|
||||
sentryDSN,
|
||||
startTraefikProxy,
|
||||
startTraefikTCPProxy,
|
||||
version
|
||||
} from './lib/common';
|
||||
import { scheduler } from './lib/scheduler';
|
||||
import { compareVersions } from 'compare-versions';
|
||||
import Graceful from '@ladjs/graceful'
|
||||
import yaml from 'js-yaml'
|
||||
import Graceful from '@ladjs/graceful';
|
||||
import yaml from 'js-yaml';
|
||||
import fs from 'fs/promises';
|
||||
import { verifyRemoteDockerEngineFn } from './routes/api/v1/destinations/handlers';
|
||||
import { checkContainer } from './lib/docker';
|
||||
@ -23,13 +36,13 @@ import * as Sentry from '@sentry/node';
|
||||
declare module 'fastify' {
|
||||
interface FastifyInstance {
|
||||
config: {
|
||||
COOLIFY_APP_ID: string,
|
||||
COOLIFY_SECRET_KEY: string,
|
||||
COOLIFY_DATABASE_URL: string,
|
||||
COOLIFY_IS_ON: string,
|
||||
COOLIFY_WHITE_LABELED: string,
|
||||
COOLIFY_WHITE_LABELED_ICON: string | null,
|
||||
COOLIFY_AUTO_UPDATE: string,
|
||||
COOLIFY_APP_ID: string;
|
||||
COOLIFY_SECRET_KEY: string;
|
||||
COOLIFY_DATABASE_URL: string;
|
||||
COOLIFY_IS_ON: string;
|
||||
COOLIFY_WHITE_LABELED: string;
|
||||
COOLIFY_WHITE_LABELED_ICON: string | null;
|
||||
COOLIFY_AUTO_UPDATE: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -38,7 +51,7 @@ const port = isDev ? 3001 : 3000;
|
||||
const host = '0.0.0.0';
|
||||
|
||||
(async () => {
|
||||
const settings = await prisma.setting.findFirst()
|
||||
const settings = await prisma.setting.findFirst();
|
||||
const fastify = Fastify({
|
||||
logger: settings?.isAPIDebuggingEnabled || false,
|
||||
trustProxy: true
|
||||
@ -49,10 +62,10 @@ const host = '0.0.0.0';
|
||||
required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'],
|
||||
properties: {
|
||||
COOLIFY_APP_ID: {
|
||||
type: 'string',
|
||||
type: 'string'
|
||||
},
|
||||
COOLIFY_SECRET_KEY: {
|
||||
type: 'string',
|
||||
type: 'string'
|
||||
},
|
||||
COOLIFY_DATABASE_URL: {
|
||||
type: 'string',
|
||||
@ -73,8 +86,7 @@ const host = '0.0.0.0';
|
||||
COOLIFY_AUTO_UPDATE: {
|
||||
type: 'string',
|
||||
default: 'false'
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
const options = {
|
||||
@ -103,13 +115,13 @@ const host = '0.0.0.0';
|
||||
fastify.register(autoLoad, {
|
||||
dir: join(__dirname, 'routes')
|
||||
});
|
||||
fastify.register(cookie)
|
||||
fastify.register(cookie);
|
||||
fastify.register(cors);
|
||||
fastify.register(socketIO, {
|
||||
cors: {
|
||||
origin: isDev ? "*" : ''
|
||||
origin: isDev ? '*' : ''
|
||||
}
|
||||
})
|
||||
});
|
||||
// To detect allowed origins
|
||||
// fastify.addHook('onRequest', async (request, reply) => {
|
||||
// console.log(request.headers.host)
|
||||
@ -131,10 +143,9 @@ const host = '0.0.0.0';
|
||||
// }
|
||||
// })
|
||||
|
||||
|
||||
try {
|
||||
await fastify.listen({ port, host })
|
||||
await socketIOServer(fastify)
|
||||
await fastify.listen({ port, host });
|
||||
await socketIOServer(fastify);
|
||||
console.log(`Coolify's API is listening on ${host}:${port}`);
|
||||
|
||||
migrateServicesToNewTemplate();
|
||||
@ -145,108 +156,103 @@ const host = '0.0.0.0';
|
||||
graceful.listen();
|
||||
|
||||
setInterval(async () => {
|
||||
if (!scheduler.workers.has('deployApplication')) {
|
||||
if (!scheduler.workers.has('deployApplication')) {
|
||||
scheduler.run('deployApplication');
|
||||
}
|
||||
}, 2000)
|
||||
}, 2000);
|
||||
|
||||
// autoUpdater
|
||||
setInterval(async () => {
|
||||
await autoUpdater()
|
||||
}, 60000 * 15)
|
||||
await autoUpdater();
|
||||
}, 60000 * 15);
|
||||
|
||||
// cleanupStorage
|
||||
setInterval(async () => {
|
||||
await cleanupStorage()
|
||||
}, 60000 * 10)
|
||||
await cleanupStorage();
|
||||
}, 60000 * 15);
|
||||
|
||||
// checkProxies, checkFluentBit & refresh templates
|
||||
setInterval(async () => {
|
||||
await checkProxies();
|
||||
await checkFluentBit();
|
||||
}, 60000)
|
||||
}, 60000);
|
||||
|
||||
// Refresh and check templates
|
||||
setInterval(async () => {
|
||||
await refreshTemplates()
|
||||
}, 60000)
|
||||
await refreshTemplates();
|
||||
}, 60000);
|
||||
|
||||
setInterval(async () => {
|
||||
await refreshTags()
|
||||
}, 60000)
|
||||
await refreshTags();
|
||||
}, 60000);
|
||||
|
||||
setInterval(async () => {
|
||||
await migrateServicesToNewTemplate()
|
||||
}, isDev ? 10000 : 60000)
|
||||
setInterval(
|
||||
async () => {
|
||||
await migrateServicesToNewTemplate();
|
||||
},
|
||||
isDev ? 10000 : 60000
|
||||
);
|
||||
|
||||
setInterval(async () => {
|
||||
await copySSLCertificates();
|
||||
}, 10000)
|
||||
|
||||
await Promise.all([
|
||||
getTagsTemplates(),
|
||||
getArch(),
|
||||
getIPAddress(),
|
||||
configureRemoteDockers(),
|
||||
])
|
||||
}, 10000);
|
||||
|
||||
await Promise.all([getTagsTemplates(), getArch(), getIPAddress(), configureRemoteDockers()]);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
async function getIPAddress() {
|
||||
const { publicIpv4, publicIpv6 } = await import('public-ip')
|
||||
const { publicIpv4, publicIpv6 } = await import('public-ip');
|
||||
try {
|
||||
const settings = await listSettings();
|
||||
if (!settings.ipv4) {
|
||||
const ipv4 = await publicIpv4({ timeout: 2000 })
|
||||
const ipv4 = await publicIpv4({ timeout: 2000 });
|
||||
console.log(`Getting public IPv4 address...`);
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } })
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } });
|
||||
}
|
||||
|
||||
if (!settings.ipv6) {
|
||||
const ipv6 = await publicIpv6({ timeout: 2000 })
|
||||
const ipv6 = await publicIpv6({ timeout: 2000 });
|
||||
console.log(`Getting public IPv6 address...`);
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } })
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } });
|
||||
}
|
||||
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
}
|
||||
async function getTagsTemplates() {
|
||||
const { default: got } = await import('got')
|
||||
const { default: got } = await import('got');
|
||||
try {
|
||||
if (isDev) {
|
||||
const templates = await fs.readFile('./devTemplates.yaml', 'utf8')
|
||||
const tags = await fs.readFile('./devTags.json', 'utf8')
|
||||
await fs.writeFile('./templates.json', JSON.stringify(yaml.load(templates)))
|
||||
await fs.writeFile('./tags.json', tags)
|
||||
console.log('[004] Tags and templates loaded in dev mode...')
|
||||
const templates = await fs.readFile('./devTemplates.yaml', 'utf8');
|
||||
const tags = await fs.readFile('./devTags.json', 'utf8');
|
||||
await fs.writeFile('./templates.json', JSON.stringify(yaml.load(templates)));
|
||||
await fs.writeFile('./tags.json', tags);
|
||||
console.log('[004] Tags and templates loaded in dev mode...');
|
||||
} else {
|
||||
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text()
|
||||
const response = await got.get('https://get.coollabs.io/coolify/service-templates.yaml').text()
|
||||
await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response)))
|
||||
await fs.writeFile('/app/tags.json', tags)
|
||||
console.log('[004] Tags and templates loaded...')
|
||||
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text();
|
||||
const response = await got
|
||||
.get('https://get.coollabs.io/coolify/service-templates.yaml')
|
||||
.text();
|
||||
await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response)));
|
||||
await fs.writeFile('/app/tags.json', tags);
|
||||
console.log('[004] Tags and templates loaded...');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log("Couldn't get latest templates.")
|
||||
console.log(error)
|
||||
console.log("Couldn't get latest templates.");
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
async function initServer() {
|
||||
const appId = process.env['COOLIFY_APP_ID'];
|
||||
const settings = await prisma.setting.findUnique({ where: { id: '0' } })
|
||||
const settings = await prisma.setting.findUnique({ where: { id: '0' } });
|
||||
try {
|
||||
if (settings.doNotTrack === true) {
|
||||
console.log('[000] Telemetry disabled...')
|
||||
|
||||
console.log('[000] Telemetry disabled...');
|
||||
} else {
|
||||
if (settings.sentryDSN !== sentryDSN) {
|
||||
await prisma.setting.update({ where: { id: '0' }, data: { sentryDSN } })
|
||||
await prisma.setting.update({ where: { id: '0' }, data: { sentryDSN } });
|
||||
}
|
||||
// Initialize Sentry
|
||||
// Sentry.init({
|
||||
@ -257,35 +263,38 @@ async function initServer() {
|
||||
// console.log('[000] Sentry initialized...')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
console.error(error);
|
||||
}
|
||||
try {
|
||||
console.log(`[001] Initializing server...`);
|
||||
await executeCommand({ command: `docker network create --attachable coolify` });
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
try {
|
||||
console.log(`[002] Cleanup stucked builds...`);
|
||||
const isOlder = compareVersions('3.8.1', version);
|
||||
if (isOlder === 1) {
|
||||
await prisma.build.updateMany({ where: { status: { in: ['running', 'queued'] } }, data: { status: 'failed' } });
|
||||
await prisma.build.updateMany({
|
||||
where: { status: { in: ['running', 'queued'] } },
|
||||
data: { status: 'failed' }
|
||||
});
|
||||
}
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
try {
|
||||
console.log('[003] Cleaning up old build sources under /tmp/build-sources/...');
|
||||
await fs.rm('/tmp/build-sources', { recursive: true, force: true })
|
||||
await fs.rm('/tmp/build-sources', { recursive: true, force: true });
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function getArch() {
|
||||
try {
|
||||
const settings = await prisma.setting.findFirst({})
|
||||
const settings = await prisma.setting.findFirst({});
|
||||
if (settings && !settings.arch) {
|
||||
console.log(`Getting architecture...`);
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } })
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } });
|
||||
}
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
async function configureRemoteDockers() {
|
||||
@ -296,37 +305,44 @@ async function configureRemoteDockers() {
|
||||
if (remoteDocker.length > 0) {
|
||||
console.log(`Verifying Remote Docker Engines...`);
|
||||
for (const docker of remoteDocker) {
|
||||
console.log('Verifying:', docker.remoteIpAddress)
|
||||
console.log('Verifying:', docker.remoteIpAddress);
|
||||
await verifyRemoteDockerEngineFn(docker.id);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function autoUpdater() {
|
||||
try {
|
||||
const { default: got } = await import('got')
|
||||
const { default: got } = await import('got');
|
||||
const currentVersion = version;
|
||||
const { coolify } = await got.get('https://get.coollabs.io/versions.json', {
|
||||
searchParams: {
|
||||
appId: process.env['COOLIFY_APP_ID'] || undefined,
|
||||
version: currentVersion
|
||||
}
|
||||
}).json()
|
||||
const { coolify } = await got
|
||||
.get('https://get.coollabs.io/versions.json', {
|
||||
searchParams: {
|
||||
appId: process.env['COOLIFY_APP_ID'] || undefined,
|
||||
version: currentVersion
|
||||
}
|
||||
})
|
||||
.json();
|
||||
const latestVersion = coolify.main.version;
|
||||
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
|
||||
if (isUpdateAvailable === 1) {
|
||||
const activeCount = 0
|
||||
const activeCount = 0;
|
||||
if (activeCount === 0) {
|
||||
if (!isDev) {
|
||||
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
|
||||
if (isAutoUpdateEnabled) {
|
||||
await executeCommand({ command: `docker pull coollabsio/coolify:${latestVersion}` })
|
||||
await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` })
|
||||
await executeCommand({ command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env` })
|
||||
await executeCommand({ shell: true, command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"` })
|
||||
await executeCommand({ command: `docker pull coollabsio/coolify:${latestVersion}` });
|
||||
await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` });
|
||||
await executeCommand({
|
||||
command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env`
|
||||
});
|
||||
await executeCommand({
|
||||
shell: true,
|
||||
command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"`
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log('Updating (not really in dev mode).');
|
||||
@ -334,7 +350,7 @@ async function autoUpdater() {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,14 +361,18 @@ async function checkFluentBit() {
|
||||
const { id } = await prisma.destinationDocker.findFirst({
|
||||
where: { engine, network: 'coolify' }
|
||||
});
|
||||
const { found } = await checkContainer({ dockerId: id, container: 'coolify-fluentbit', remove: true });
|
||||
const { found } = await checkContainer({
|
||||
dockerId: id,
|
||||
container: 'coolify-fluentbit',
|
||||
remove: true
|
||||
});
|
||||
if (!found) {
|
||||
await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` });
|
||||
await executeCommand({ command: `docker compose up -d fluent-bit` });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
async function checkProxies() {
|
||||
@ -368,7 +388,7 @@ async function checkProxies() {
|
||||
where: { engine, network: 'coolify', isCoolifyProxyUsed: true }
|
||||
});
|
||||
if (localDocker) {
|
||||
portReachable = await isReachable(80, { host: ipv4 || ipv6 })
|
||||
portReachable = await isReachable(80, { host: ipv4 || ipv6 });
|
||||
if (!portReachable) {
|
||||
await startTraefikProxy(localDocker.id);
|
||||
}
|
||||
@ -380,14 +400,14 @@ async function checkProxies() {
|
||||
if (remoteDocker.length > 0) {
|
||||
for (const docker of remoteDocker) {
|
||||
if (docker.isCoolifyProxyUsed) {
|
||||
portReachable = await isReachable(80, { host: docker.remoteIpAddress })
|
||||
portReachable = await isReachable(80, { host: docker.remoteIpAddress });
|
||||
if (!portReachable) {
|
||||
await startTraefikProxy(docker.id);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await createRemoteEngineConfiguration(docker.id)
|
||||
} catch (error) { }
|
||||
await createRemoteEngineConfiguration(docker.id);
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
// TCP Proxies
|
||||
@ -426,80 +446,105 @@ async function checkProxies() {
|
||||
// await startTraefikTCPProxy(destinationDocker, id, publicPort, 9000);
|
||||
// }
|
||||
// }
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
async function copySSLCertificates() {
|
||||
try {
|
||||
const pAll = await import('p-all');
|
||||
const actions = []
|
||||
const certificates = await prisma.certificate.findMany({ include: { team: true } })
|
||||
const teamIds = certificates.map(c => c.teamId)
|
||||
const destinations = await prisma.destinationDocker.findMany({ where: { isCoolifyProxyUsed: true, teams: { some: { id: { in: [...teamIds] } } } } })
|
||||
const actions = [];
|
||||
const certificates = await prisma.certificate.findMany({ include: { team: true } });
|
||||
const teamIds = certificates.map((c) => c.teamId);
|
||||
const destinations = await prisma.destinationDocker.findMany({
|
||||
where: { isCoolifyProxyUsed: true, teams: { some: { id: { in: [...teamIds] } } } }
|
||||
});
|
||||
for (const certificate of certificates) {
|
||||
const { id, key, cert } = certificate
|
||||
const decryptedKey = decrypt(key)
|
||||
await fs.writeFile(`/tmp/${id}-key.pem`, decryptedKey)
|
||||
await fs.writeFile(`/tmp/${id}-cert.pem`, cert)
|
||||
const { id, key, cert } = certificate;
|
||||
const decryptedKey = decrypt(key);
|
||||
await fs.writeFile(`/tmp/${id}-key.pem`, decryptedKey);
|
||||
await fs.writeFile(`/tmp/${id}-cert.pem`, cert);
|
||||
for (const destination of destinations) {
|
||||
if (destination.remoteEngine) {
|
||||
if (destination.remoteVerified) {
|
||||
const { id: dockerId, remoteIpAddress } = destination
|
||||
actions.push(async () => copyRemoteCertificates(id, dockerId, remoteIpAddress))
|
||||
const { id: dockerId, remoteIpAddress } = destination;
|
||||
actions.push(async () => copyRemoteCertificates(id, dockerId, remoteIpAddress));
|
||||
}
|
||||
} else {
|
||||
actions.push(async () => copyLocalCertificates(id))
|
||||
actions.push(async () => copyLocalCertificates(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
await pAll.default(actions, { concurrency: 1 })
|
||||
await pAll.default(actions, { concurrency: 1 });
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
} finally {
|
||||
await executeCommand({ command: `find /tmp/ -maxdepth 1 -type f -name '*-*.pem' -delete` })
|
||||
await executeCommand({ command: `find /tmp/ -maxdepth 1 -type f -name '*-*.pem' -delete` });
|
||||
}
|
||||
}
|
||||
|
||||
async function copyRemoteCertificates(id: string, dockerId: string, remoteIpAddress: string) {
|
||||
try {
|
||||
await executeCommand({ command: `scp /tmp/${id}-cert.pem /tmp/${id}-key.pem ${remoteIpAddress}:/tmp/` })
|
||||
await executeCommand({ sshCommand: true, shell: true, dockerId, command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'` })
|
||||
await executeCommand({ sshCommand: true, dockerId, command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/` })
|
||||
await executeCommand({ sshCommand: true, dockerId, command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/` })
|
||||
await executeCommand({
|
||||
command: `scp /tmp/${id}-cert.pem /tmp/${id}-key.pem ${remoteIpAddress}:/tmp/`
|
||||
});
|
||||
await executeCommand({
|
||||
sshCommand: true,
|
||||
shell: true,
|
||||
dockerId,
|
||||
command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`
|
||||
});
|
||||
await executeCommand({
|
||||
sshCommand: true,
|
||||
dockerId,
|
||||
command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||
});
|
||||
await executeCommand({
|
||||
sshCommand: true,
|
||||
dockerId,
|
||||
command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||
});
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
console.log({ error });
|
||||
}
|
||||
}
|
||||
async function copyLocalCertificates(id: string) {
|
||||
try {
|
||||
await executeCommand({ command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`, shell: true })
|
||||
await executeCommand({ command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/` })
|
||||
await executeCommand({ command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/` })
|
||||
await executeCommand({
|
||||
command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`,
|
||||
shell: true
|
||||
});
|
||||
await executeCommand({
|
||||
command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||
});
|
||||
await executeCommand({
|
||||
command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||
});
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
console.log({ error });
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanupStorage() {
|
||||
const destinationDockers = await prisma.destinationDocker.findMany();
|
||||
let enginesDone = new Set()
|
||||
let enginesDone = new Set();
|
||||
for (const destination of destinationDockers) {
|
||||
if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) return
|
||||
if (destination.engine) enginesDone.add(destination.engine)
|
||||
if (destination.remoteIpAddress) enginesDone.add(destination.remoteIpAddress)
|
||||
|
||||
if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) return;
|
||||
if (destination.engine) enginesDone.add(destination.engine);
|
||||
if (destination.remoteIpAddress) enginesDone.add(destination.remoteIpAddress);
|
||||
let force = false;
|
||||
let lowDiskSpace = false;
|
||||
try {
|
||||
let stdout = null
|
||||
let stdout = null;
|
||||
if (!isDev) {
|
||||
const output = await executeCommand({ dockerId: destination.id, command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`, shell: true })
|
||||
const output = await executeCommand({
|
||||
dockerId: destination.id,
|
||||
command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`,
|
||||
shell: true
|
||||
});
|
||||
stdout = output.stdout;
|
||||
} else {
|
||||
const output = await executeCommand({
|
||||
command:
|
||||
`df -kPT /`
|
||||
command: `df -kPT /`
|
||||
});
|
||||
stdout = output.stdout;
|
||||
}
|
||||
@ -531,7 +576,7 @@ async function cleanupStorage() {
|
||||
lowDiskSpace = true;
|
||||
}
|
||||
}
|
||||
} catch (error) { }
|
||||
await cleanupDockerStorage(destination.id, lowDiskSpace, false)
|
||||
} catch (error) {}
|
||||
await cleanupDockerStorage(destination.id, lowDiskSpace, force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,26 @@ import crypto from 'crypto';
|
||||
import fs from 'fs/promises';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
import { copyBaseConfigurationFiles, makeLabelForSimpleDockerfile, makeLabelForStandaloneApplication, saveBuildLog, saveDockerRegistryCredentials, setDefaultConfiguration } from '../lib/buildPacks/common';
|
||||
import { createDirectories, decrypt, defaultComposeConfiguration, getDomain, prisma, decryptApplication, isDev, pushToRegistry, executeCommand } from '../lib/common';
|
||||
import {
|
||||
copyBaseConfigurationFiles,
|
||||
makeLabelForSimpleDockerfile,
|
||||
makeLabelForStandaloneApplication,
|
||||
saveBuildLog,
|
||||
saveDockerRegistryCredentials,
|
||||
setDefaultConfiguration
|
||||
} from '../lib/buildPacks/common';
|
||||
import {
|
||||
createDirectories,
|
||||
decrypt,
|
||||
defaultComposeConfiguration,
|
||||
getDomain,
|
||||
prisma,
|
||||
decryptApplication,
|
||||
isDev,
|
||||
pushToRegistry,
|
||||
executeCommand,
|
||||
generateSecrets
|
||||
} from '../lib/common';
|
||||
import * as importers from '../lib/importers';
|
||||
import * as buildpacks from '../lib/buildPacks';
|
||||
|
||||
@ -14,33 +32,55 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
if (message === 'error') throw new Error('oops');
|
||||
if (message === 'cancel') {
|
||||
parentPort.postMessage('cancelled');
|
||||
await prisma.$disconnect()
|
||||
await prisma.$disconnect();
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
const pThrottle = await import('p-throttle')
|
||||
const pThrottle = await import('p-throttle');
|
||||
const throttle = pThrottle.default({
|
||||
limit: 1,
|
||||
interval: 2000
|
||||
});
|
||||
|
||||
|
||||
const th = throttle(async () => {
|
||||
try {
|
||||
const queuedBuilds = await prisma.build.findMany({ where: { status: { in: ['queued', 'running'] } }, orderBy: { createdAt: 'asc' } });
|
||||
const { concurrentBuilds } = await prisma.setting.findFirst({})
|
||||
const queuedBuilds = await prisma.build.findMany({
|
||||
where: { status: { in: ['queued', 'running'] } },
|
||||
orderBy: { createdAt: 'asc' }
|
||||
});
|
||||
const { concurrentBuilds } = await prisma.setting.findFirst({});
|
||||
if (queuedBuilds.length > 0) {
|
||||
parentPort.postMessage({ deploying: true });
|
||||
const concurrency = concurrentBuilds;
|
||||
const pAll = await import('p-all');
|
||||
const actions = []
|
||||
const actions = [];
|
||||
|
||||
for (const queueBuild of queuedBuilds) {
|
||||
actions.push(async () => {
|
||||
let application = await prisma.application.findUnique({ where: { id: queueBuild.applicationId }, include: { dockerRegistry: true, destinationDocker: true, gitSource: { include: { githubApp: true, gitlabApp: true } }, persistentStorage: true, secrets: true, settings: true, teams: true } })
|
||||
let application = await prisma.application.findUnique({
|
||||
where: { id: queueBuild.applicationId },
|
||||
include: {
|
||||
dockerRegistry: true,
|
||||
destinationDocker: true,
|
||||
gitSource: { include: { githubApp: true, gitlabApp: true } },
|
||||
persistentStorage: true,
|
||||
secrets: true,
|
||||
settings: true,
|
||||
teams: true
|
||||
}
|
||||
});
|
||||
|
||||
let { id: buildId, type, gitSourceId, sourceBranch = null, pullmergeRequestId = null, previewApplicationId = null, forceRebuild, sourceRepository = null } = queueBuild
|
||||
application = decryptApplication(application)
|
||||
let {
|
||||
id: buildId,
|
||||
type,
|
||||
gitSourceId,
|
||||
sourceBranch = null,
|
||||
pullmergeRequestId = null,
|
||||
previewApplicationId = null,
|
||||
forceRebuild,
|
||||
sourceRepository = null
|
||||
} = queueBuild;
|
||||
application = decryptApplication(application);
|
||||
|
||||
if (!gitSourceId && application.simpleDockerfile) {
|
||||
const {
|
||||
@ -53,77 +93,71 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
exposePort,
|
||||
simpleDockerfile,
|
||||
dockerRegistry
|
||||
} = application
|
||||
} = application;
|
||||
const { workdir } = await createDirectories({ repository: applicationId, buildId });
|
||||
try {
|
||||
if (queueBuild.status === 'running') {
|
||||
await saveBuildLog({ line: 'Building halted, restarting...', buildId, applicationId: application.id });
|
||||
await saveBuildLog({
|
||||
line: 'Building halted, restarting...',
|
||||
buildId,
|
||||
applicationId: application.id
|
||||
});
|
||||
}
|
||||
const volumes =
|
||||
persistentStorage?.map((storage) => {
|
||||
if (storage.oldPath) {
|
||||
return `${applicationId}${storage.path.replace(/\//gi, '-').replace('-app', '')}:${storage.path}`;
|
||||
return `${applicationId}${storage.path
|
||||
.replace(/\//gi, '-')
|
||||
.replace('-app', '')}:${storage.path}`;
|
||||
}
|
||||
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
|
||||
}) || [];
|
||||
|
||||
if (destinationDockerId) {
|
||||
await prisma.build.update({ where: { id: buildId }, data: { status: 'running' } });
|
||||
await prisma.build.update({
|
||||
where: { id: buildId },
|
||||
data: { status: 'running' }
|
||||
});
|
||||
try {
|
||||
const { stdout: containers } = await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker ps -a --filter 'label=com.docker.compose.service=${applicationId}' --format {{.ID}}`
|
||||
})
|
||||
});
|
||||
if (containers) {
|
||||
const containerArray = containers.split('\n');
|
||||
if (containerArray.length > 0) {
|
||||
for (const container of containerArray) {
|
||||
await executeCommand({ dockerId: destinationDockerId, command: `docker stop -t 0 ${container}` })
|
||||
await executeCommand({ dockerId: destinationDockerId, command: `docker rm --force ${container}` })
|
||||
await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker stop -t 0 ${container}`
|
||||
});
|
||||
await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker rm --force ${container}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
const envs = [
|
||||
`PORT=${port}`
|
||||
];
|
||||
let envs = [];
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
envs.push(`${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
envs.push(`${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
envs.push(`${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
envs = [
|
||||
...envs,
|
||||
...generateSecrets(secrets, pullmergeRequestId, false, port)
|
||||
];
|
||||
}
|
||||
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
||||
|
||||
let envFound = false;
|
||||
try {
|
||||
envFound = !!(await fs.stat(`${workdir}/.env`));
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, simpleDockerfile);
|
||||
if (dockerRegistry) {
|
||||
const { url, username, password } = dockerRegistry
|
||||
await saveDockerRegistryCredentials({ url, username, password, workdir })
|
||||
const { url, username, password } = dockerRegistry;
|
||||
await saveDockerRegistryCredentials({ url, username, password, workdir });
|
||||
}
|
||||
|
||||
const labels = makeLabelForSimpleDockerfile({
|
||||
applicationId,
|
||||
type,
|
||||
port: exposePort ? `${exposePort}:${port}` : port,
|
||||
port: exposePort ? `${exposePort}:${port}` : port
|
||||
});
|
||||
try {
|
||||
const composeVolumes = volumes.map((volume) => {
|
||||
@ -138,17 +172,17 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
services: {
|
||||
[applicationId]: {
|
||||
build: {
|
||||
context: workdir,
|
||||
context: workdir
|
||||
},
|
||||
image: `${applicationId}:${buildId}`,
|
||||
container_name: applicationId,
|
||||
volumes,
|
||||
labels,
|
||||
env_file: envFound ? [`${workdir}/.env`] : [],
|
||||
environment: envs,
|
||||
depends_on: [],
|
||||
expose: [port],
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
...defaultComposeConfiguration(destinationDocker.network),
|
||||
...defaultComposeConfiguration(destinationDocker.network)
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
@ -159,11 +193,15 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
volumes: Object.assign({}, ...composeVolumes)
|
||||
};
|
||||
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile));
|
||||
await executeCommand({ debug: true, dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` })
|
||||
await executeCommand({
|
||||
debug: true,
|
||||
dockerId: destinationDocker.id,
|
||||
command: `docker compose --project-directory ${workdir} up -d`
|
||||
});
|
||||
await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId });
|
||||
} catch (error) {
|
||||
await saveBuildLog({ line: error, buildId, applicationId });
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (foundBuild) {
|
||||
await prisma.build.update({
|
||||
where: { id: buildId },
|
||||
@ -174,10 +212,9 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
}
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (foundBuild) {
|
||||
await prisma.build.update({
|
||||
where: { id: buildId },
|
||||
@ -190,7 +227,11 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
await saveBuildLog({ line: error, buildId, applicationId: application.id });
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
await saveBuildLog({ line: error.message, buildId, applicationId: application.id });
|
||||
await saveBuildLog({
|
||||
line: error.message,
|
||||
buildId,
|
||||
applicationId: application.id
|
||||
});
|
||||
}
|
||||
await fs.rm(workdir, { recursive: true, force: true });
|
||||
return;
|
||||
@ -199,9 +240,13 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
if (application.dockerRegistryImageName) {
|
||||
const customTag = application.dockerRegistryImageName.split(':')[1] || buildId;
|
||||
const imageName = application.dockerRegistryImageName.split(':')[0];
|
||||
await saveBuildLog({ line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`, buildId, applicationId: application.id });
|
||||
await pushToRegistry(application, workdir, buildId, imageName, customTag)
|
||||
await saveBuildLog({ line: "Success", buildId, applicationId: application.id });
|
||||
await saveBuildLog({
|
||||
line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`,
|
||||
buildId,
|
||||
applicationId: application.id
|
||||
});
|
||||
await pushToRegistry(application, workdir, buildId, imageName, customTag);
|
||||
await saveBuildLog({ line: 'Success', buildId, applicationId: application.id });
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.stdout) {
|
||||
@ -212,12 +257,15 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
}
|
||||
} finally {
|
||||
await fs.rm(workdir, { recursive: true, force: true });
|
||||
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
|
||||
await prisma.build.update({
|
||||
where: { id: buildId },
|
||||
data: { status: 'success' }
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const originalApplicationId = application.id
|
||||
const originalApplicationId = application.id;
|
||||
const {
|
||||
id: applicationId,
|
||||
name,
|
||||
@ -241,7 +289,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
deploymentType,
|
||||
gitCommitHash,
|
||||
dockerRegistry
|
||||
} = application
|
||||
} = application;
|
||||
|
||||
let {
|
||||
branch,
|
||||
@ -257,7 +305,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
dockerComposeFileLocation,
|
||||
dockerComposeConfiguration,
|
||||
denoMainFile
|
||||
} = application
|
||||
} = application;
|
||||
|
||||
let imageId = applicationId;
|
||||
let domain = getDomain(fqdn);
|
||||
@ -272,9 +320,11 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
let imageFoundRemotely = false;
|
||||
|
||||
if (pullmergeRequestId) {
|
||||
const previewApplications = await prisma.previewApplication.findMany({ where: { applicationId: originalApplicationId, pullmergeRequestId } })
|
||||
const previewApplications = await prisma.previewApplication.findMany({
|
||||
where: { applicationId: originalApplicationId, pullmergeRequestId }
|
||||
});
|
||||
if (previewApplications.length > 0) {
|
||||
previewApplicationId = previewApplications[0].id
|
||||
previewApplicationId = previewApplications[0].id;
|
||||
}
|
||||
// Previews, we need to get the source branch and set subdomain
|
||||
branch = sourceBranch;
|
||||
@ -285,7 +335,11 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
const { workdir, repodir } = await createDirectories({ repository, buildId });
|
||||
try {
|
||||
if (queueBuild.status === 'running') {
|
||||
await saveBuildLog({ line: 'Building halted, restarting...', buildId, applicationId: application.id });
|
||||
await saveBuildLog({
|
||||
line: 'Building halted, restarting...',
|
||||
buildId,
|
||||
applicationId: application.id
|
||||
});
|
||||
}
|
||||
|
||||
const currentHash = crypto
|
||||
@ -323,15 +377,16 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
const volumes =
|
||||
persistentStorage?.map((storage) => {
|
||||
if (storage.oldPath) {
|
||||
return `${applicationId}${storage.path.replace(/\//gi, '-').replace('-app', '')}:${storage.path}`;
|
||||
return `${applicationId}${storage.path
|
||||
.replace(/\//gi, '-')
|
||||
.replace('-app', '')}:${storage.path}`;
|
||||
}
|
||||
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
|
||||
}) || [];
|
||||
|
||||
|
||||
try {
|
||||
dockerComposeConfiguration = JSON.parse(dockerComposeConfiguration)
|
||||
} catch (error) { }
|
||||
dockerComposeConfiguration = JSON.parse(dockerComposeConfiguration);
|
||||
} catch (error) {}
|
||||
let deployNeeded = true;
|
||||
let destinationType;
|
||||
|
||||
@ -339,7 +394,10 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
destinationType = 'docker';
|
||||
}
|
||||
if (destinationType === 'docker') {
|
||||
await prisma.build.update({ where: { id: buildId }, data: { status: 'running' } });
|
||||
await prisma.build.update({
|
||||
where: { id: buildId },
|
||||
data: { status: 'running' }
|
||||
});
|
||||
|
||||
const configuration = await setDefaultConfiguration(application);
|
||||
|
||||
@ -361,6 +419,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
githubAppId: gitSource.githubApp?.id,
|
||||
gitlabAppId: gitSource.gitlabApp?.id,
|
||||
customPort: gitSource.customPort,
|
||||
customUser: gitSource.customUser,
|
||||
gitCommitHash,
|
||||
configuration,
|
||||
repository,
|
||||
@ -381,10 +440,10 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
tag = `${commit.slice(0, 7)}-${pullmergeRequestId}`;
|
||||
}
|
||||
if (application.dockerRegistryImageName) {
|
||||
imageName = application.dockerRegistryImageName.split(':')[0]
|
||||
customTag = application.dockerRegistryImageName.split(':')[1] || tag
|
||||
imageName = application.dockerRegistryImageName.split(':')[0];
|
||||
customTag = application.dockerRegistryImageName.split(':')[1] || tag;
|
||||
} else {
|
||||
customTag = tag
|
||||
customTag = tag;
|
||||
imageName = applicationId;
|
||||
}
|
||||
|
||||
@ -394,13 +453,17 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
|
||||
try {
|
||||
await prisma.build.update({ where: { id: buildId }, data: { commit } });
|
||||
} catch (err) { }
|
||||
} catch (err) {}
|
||||
|
||||
if (!pullmergeRequestId) {
|
||||
if (configHash !== currentHash) {
|
||||
deployNeeded = true;
|
||||
if (configHash) {
|
||||
await saveBuildLog({ line: 'Configuration changed', buildId, applicationId });
|
||||
await saveBuildLog({
|
||||
line: 'Configuration changed',
|
||||
buildId,
|
||||
applicationId
|
||||
});
|
||||
}
|
||||
} else {
|
||||
deployNeeded = false;
|
||||
@ -413,30 +476,43 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
await executeCommand({
|
||||
dockerId: destinationDocker.id,
|
||||
command: `docker image inspect ${applicationId}:${tag}`
|
||||
})
|
||||
});
|
||||
imageFoundLocally = true;
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
if (dockerRegistry) {
|
||||
const { url, username, password } = dockerRegistry
|
||||
location = await saveDockerRegistryCredentials({ url, username, password, workdir })
|
||||
const { url, username, password } = dockerRegistry;
|
||||
location = await saveDockerRegistryCredentials({
|
||||
url,
|
||||
username,
|
||||
password,
|
||||
workdir
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await executeCommand({
|
||||
dockerId: destinationDocker.id,
|
||||
command: `docker ${location ? `--config ${location}` : ''} pull ${imageName}:${customTag}`
|
||||
})
|
||||
command: `docker ${
|
||||
location ? `--config ${location}` : ''
|
||||
} pull ${imageName}:${customTag}`
|
||||
});
|
||||
imageFoundRemotely = true;
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
let imageFound = `${applicationId}:${tag}`
|
||||
let imageFound = `${applicationId}:${tag}`;
|
||||
if (imageFoundRemotely) {
|
||||
imageFound = `${imageName}:${customTag}`
|
||||
imageFound = `${imageName}:${customTag}`;
|
||||
}
|
||||
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage);
|
||||
await copyBaseConfigurationFiles(
|
||||
buildPack,
|
||||
workdir,
|
||||
buildId,
|
||||
applicationId,
|
||||
baseImage
|
||||
);
|
||||
const labels = makeLabelForStandaloneApplication({
|
||||
applicationId,
|
||||
fqdn,
|
||||
@ -455,7 +531,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
baseDirectory,
|
||||
publishDirectory
|
||||
});
|
||||
if (forceRebuild) deployNeeded = true
|
||||
if (forceRebuild) deployNeeded = true;
|
||||
if ((!imageFoundLocally && !imageFoundRemotely) || deployNeeded) {
|
||||
if (buildpacks[buildPack])
|
||||
await buildpacks[buildPack]({
|
||||
@ -496,17 +572,30 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
baseImage,
|
||||
baseBuildImage,
|
||||
deploymentType,
|
||||
forceRebuild
|
||||
});
|
||||
else {
|
||||
await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId });
|
||||
await saveBuildLog({
|
||||
line: `Build pack ${buildPack} not found`,
|
||||
buildId,
|
||||
applicationId
|
||||
});
|
||||
throw new Error(`Build pack ${buildPack} not found.`);
|
||||
}
|
||||
} else {
|
||||
if (imageFoundRemotely || deployNeeded) {
|
||||
await saveBuildLog({ line: `Container image ${imageFound} found in Docker Registry - reuising it`, buildId, applicationId });
|
||||
await saveBuildLog({
|
||||
line: `Container image ${imageFound} found in Docker Registry - reuising it`,
|
||||
buildId,
|
||||
applicationId
|
||||
});
|
||||
} else {
|
||||
if (imageFoundLocally || deployNeeded) {
|
||||
await saveBuildLog({ line: `Container image ${imageFound} found locally - reuising it`, buildId, applicationId });
|
||||
await saveBuildLog({
|
||||
line: `Container image ${imageFound} found locally - reuising it`,
|
||||
buildId,
|
||||
applicationId
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -516,13 +605,19 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
const { stdout: containers } = await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker ps -a --filter 'label=coolify.applicationId=${applicationId}' --format {{.ID}}`
|
||||
})
|
||||
});
|
||||
if (containers) {
|
||||
const containerArray = containers.split('\n');
|
||||
if (containerArray.length > 0) {
|
||||
for (const container of containerArray) {
|
||||
await executeCommand({ dockerId: destinationDockerId, command: `docker stop -t 0 ${container}` })
|
||||
await executeCommand({ dockerId: destinationDockerId, command: `docker rm --force ${container}` })
|
||||
await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker stop -t 0 ${container}`
|
||||
});
|
||||
await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker rm --force ${container}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -530,17 +625,25 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
//
|
||||
}
|
||||
try {
|
||||
console.log({ debug })
|
||||
await executeCommand({ debug, buildId, applicationId, dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` })
|
||||
await executeCommand({
|
||||
debug,
|
||||
buildId,
|
||||
applicationId,
|
||||
dockerId: destinationDocker.id,
|
||||
command: `docker compose --project-directory ${workdir} up -d`
|
||||
});
|
||||
await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId });
|
||||
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
|
||||
await prisma.build.update({
|
||||
where: { id: buildId },
|
||||
data: { status: 'success' }
|
||||
});
|
||||
await prisma.application.update({
|
||||
where: { id: applicationId },
|
||||
data: { configHash: currentHash }
|
||||
});
|
||||
} catch (error) {
|
||||
await saveBuildLog({ line: error, buildId, applicationId });
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (foundBuild) {
|
||||
await prisma.build.update({
|
||||
where: { id: buildId },
|
||||
@ -551,55 +654,42 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
}
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
const { stdout: containers } = await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker ps -a --filter 'label=com.docker.compose.service=${pullmergeRequestId ? imageId : applicationId}' --format {{.ID}}`
|
||||
})
|
||||
command: `docker ps -a --filter 'label=com.docker.compose.service=${
|
||||
pullmergeRequestId ? imageId : applicationId
|
||||
}' --format {{.ID}}`
|
||||
});
|
||||
if (containers) {
|
||||
const containerArray = containers.split('\n');
|
||||
if (containerArray.length > 0) {
|
||||
for (const container of containerArray) {
|
||||
await executeCommand({ dockerId: destinationDockerId, command: `docker stop -t 0 ${container}` })
|
||||
await executeCommand({ dockerId: destinationDockerId, command: `docker rm --force ${container}` })
|
||||
await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker stop -t 0 ${container}`
|
||||
});
|
||||
await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker rm --force ${container}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
const envs = [
|
||||
`PORT=${port}`
|
||||
];
|
||||
let envs = [];
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
envs.push(`${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
envs.push(`${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
envs.push(`${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
envs = [
|
||||
...envs,
|
||||
...generateSecrets(secrets, pullmergeRequestId, false, port)
|
||||
];
|
||||
}
|
||||
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
||||
if (dockerRegistry) {
|
||||
const { url, username, password } = dockerRegistry
|
||||
await saveDockerRegistryCredentials({ url, username, password, workdir })
|
||||
}
|
||||
|
||||
let envFound = false;
|
||||
try {
|
||||
envFound = !!(await fs.stat(`${workdir}/.env`));
|
||||
} catch (error) {
|
||||
//
|
||||
const { url, username, password } = dockerRegistry;
|
||||
await saveDockerRegistryCredentials({ url, username, password, workdir });
|
||||
}
|
||||
try {
|
||||
const composeVolumes = volumes.map((volume) => {
|
||||
@ -616,12 +706,12 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
image: imageFound,
|
||||
container_name: imageId,
|
||||
volumes,
|
||||
env_file: envFound ? [`${workdir}/.env`] : [],
|
||||
environment: envs,
|
||||
labels,
|
||||
depends_on: [],
|
||||
expose: [port],
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
...defaultComposeConfiguration(destinationDocker.network),
|
||||
...defaultComposeConfiguration(destinationDocker.network)
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
@ -632,11 +722,15 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
volumes: Object.assign({}, ...composeVolumes)
|
||||
};
|
||||
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile));
|
||||
await executeCommand({ debug, dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` })
|
||||
await executeCommand({
|
||||
debug,
|
||||
dockerId: destinationDocker.id,
|
||||
command: `docker compose --project-directory ${workdir} up -d`
|
||||
});
|
||||
await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId });
|
||||
} catch (error) {
|
||||
await saveBuildLog({ line: error, buildId, applicationId });
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (foundBuild) {
|
||||
await prisma.build.update({
|
||||
where: { id: buildId },
|
||||
@ -648,14 +742,15 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
if (!pullmergeRequestId) await prisma.application.update({
|
||||
where: { id: applicationId },
|
||||
data: { configHash: currentHash }
|
||||
});
|
||||
if (!pullmergeRequestId)
|
||||
await prisma.application.update({
|
||||
where: { id: applicationId },
|
||||
data: { configHash: currentHash }
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
|
||||
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (foundBuild) {
|
||||
await prisma.build.update({
|
||||
where: { id: buildId },
|
||||
@ -668,16 +763,24 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
await saveBuildLog({ line: error, buildId, applicationId: application.id });
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
await saveBuildLog({ line: error.message, buildId, applicationId: application.id });
|
||||
await saveBuildLog({
|
||||
line: error.message,
|
||||
buildId,
|
||||
applicationId: application.id
|
||||
});
|
||||
}
|
||||
await fs.rm(workdir, { recursive: true, force: true });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (application.dockerRegistryImageName && (!imageFoundRemotely || forceRebuild)) {
|
||||
await saveBuildLog({ line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`, buildId, applicationId: application.id });
|
||||
await pushToRegistry(application, workdir, tag, imageName, customTag)
|
||||
await saveBuildLog({ line: "Success", buildId, applicationId: application.id });
|
||||
await saveBuildLog({
|
||||
line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`,
|
||||
buildId,
|
||||
applicationId: application.id
|
||||
});
|
||||
await pushToRegistry(application, workdir, tag, imageName, customTag);
|
||||
await saveBuildLog({ line: 'Success', buildId, applicationId: application.id });
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.stdout) {
|
||||
@ -686,21 +789,20 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
if (error.stderr) {
|
||||
await saveBuildLog({ line: error.stderr, buildId, applicationId });
|
||||
}
|
||||
|
||||
} finally {
|
||||
await fs.rm(workdir, { recursive: true, force: true });
|
||||
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
|
||||
}
|
||||
});
|
||||
}
|
||||
await pAll.default(actions, { concurrency })
|
||||
await pAll.default(actions, { concurrency });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
})
|
||||
});
|
||||
while (true) {
|
||||
await th()
|
||||
await th();
|
||||
}
|
||||
} else process.exit(0);
|
||||
})();
|
||||
|
@ -1,6 +1,18 @@
|
||||
import { base64Encode, decrypt, encrypt, executeCommand, generateTimestamp, getDomain, isARM, isDev, prisma, version } from "../common";
|
||||
import {
|
||||
base64Encode,
|
||||
decrypt,
|
||||
encrypt,
|
||||
executeCommand,
|
||||
generateSecrets,
|
||||
generateTimestamp,
|
||||
getDomain,
|
||||
isARM,
|
||||
isDev,
|
||||
prisma,
|
||||
version
|
||||
} from '../common';
|
||||
import { promises as fs } from 'fs';
|
||||
import { day } from "../dayjs";
|
||||
import { day } from '../dayjs';
|
||||
|
||||
const staticApps = ['static', 'react', 'vuejs', 'svelte', 'gatsby', 'astro', 'eleventy'];
|
||||
const nodeBased = [
|
||||
@ -17,7 +29,10 @@ const nodeBased = [
|
||||
'nextjs'
|
||||
];
|
||||
|
||||
export function setDefaultBaseImage(buildPack: string | null, deploymentType: string | null = null) {
|
||||
export function setDefaultBaseImage(
|
||||
buildPack: string | null,
|
||||
deploymentType: string | null = null
|
||||
) {
|
||||
const nodeVersions = [
|
||||
{
|
||||
value: 'node:lts',
|
||||
@ -316,8 +331,8 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
||||
{
|
||||
value: 'heroku/builder-classic:22',
|
||||
label: 'heroku/builder-classic:22'
|
||||
},
|
||||
]
|
||||
}
|
||||
];
|
||||
let payload: any = {
|
||||
baseImage: null,
|
||||
baseBuildImage: null,
|
||||
@ -327,7 +342,9 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
||||
if (nodeBased.includes(buildPack)) {
|
||||
if (deploymentType === 'static') {
|
||||
payload.baseImage = isARM(process.arch) ? 'nginx:alpine' : 'webdevops/nginx:alpine';
|
||||
payload.baseImages = isARM(process.arch) ? staticVersions.filter((version) => !version.value.includes('webdevops')) : staticVersions;
|
||||
payload.baseImages = isARM(process.arch)
|
||||
? staticVersions.filter((version) => !version.value.includes('webdevops'))
|
||||
: staticVersions;
|
||||
payload.baseBuildImage = 'node:lts';
|
||||
payload.baseBuildImages = nodeVersions;
|
||||
} else {
|
||||
@ -339,7 +356,9 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
||||
}
|
||||
if (staticApps.includes(buildPack)) {
|
||||
payload.baseImage = isARM(process.arch) ? 'nginx:alpine' : 'webdevops/nginx:alpine';
|
||||
payload.baseImages = isARM(process.arch) ? staticVersions.filter((version) => !version.value.includes('webdevops')) : staticVersions;
|
||||
payload.baseImages = isARM(process.arch)
|
||||
? staticVersions.filter((version) => !version.value.includes('webdevops'))
|
||||
: staticVersions;
|
||||
payload.baseBuildImage = 'node:lts';
|
||||
payload.baseBuildImages = nodeVersions;
|
||||
}
|
||||
@ -357,12 +376,20 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
||||
payload.baseImage = 'denoland/deno:latest';
|
||||
}
|
||||
if (buildPack === 'php') {
|
||||
payload.baseImage = isARM(process.arch) ? 'php:8.1-fpm-alpine' : 'webdevops/php-apache:8.2-alpine';
|
||||
payload.baseImages = isARM(process.arch) ? phpVersions.filter((version) => !version.value.includes('webdevops')) : phpVersions
|
||||
payload.baseImage = isARM(process.arch)
|
||||
? 'php:8.1-fpm-alpine'
|
||||
: 'webdevops/php-apache:8.2-alpine';
|
||||
payload.baseImages = isARM(process.arch)
|
||||
? phpVersions.filter((version) => !version.value.includes('webdevops'))
|
||||
: phpVersions;
|
||||
}
|
||||
if (buildPack === 'laravel') {
|
||||
payload.baseImage = isARM(process.arch) ? 'php:8.1-fpm-alpine' : 'webdevops/php-apache:8.2-alpine';
|
||||
payload.baseImages = isARM(process.arch) ? phpVersions.filter((version) => !version.value.includes('webdevops')) : phpVersions
|
||||
payload.baseImage = isARM(process.arch)
|
||||
? 'php:8.1-fpm-alpine'
|
||||
: 'webdevops/php-apache:8.2-alpine';
|
||||
payload.baseImages = isARM(process.arch)
|
||||
? phpVersions.filter((version) => !version.value.includes('webdevops'))
|
||||
: phpVersions;
|
||||
payload.baseBuildImage = 'node:18';
|
||||
payload.baseBuildImages = nodeVersions;
|
||||
}
|
||||
@ -405,7 +432,8 @@ export const setDefaultConfiguration = async (data: any) => {
|
||||
if (!publishDirectory) publishDirectory = template?.publishDirectory || null;
|
||||
if (baseDirectory) {
|
||||
if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`;
|
||||
if (baseDirectory.endsWith('/') && baseDirectory !== '/') baseDirectory = baseDirectory.slice(0, -1);
|
||||
if (baseDirectory.endsWith('/') && baseDirectory !== '/')
|
||||
baseDirectory = baseDirectory.slice(0, -1);
|
||||
}
|
||||
if (dockerFileLocation) {
|
||||
if (!dockerFileLocation.startsWith('/')) dockerFileLocation = `/${dockerFileLocation}`;
|
||||
@ -414,8 +442,10 @@ export const setDefaultConfiguration = async (data: any) => {
|
||||
dockerFileLocation = '/Dockerfile';
|
||||
}
|
||||
if (dockerComposeFileLocation) {
|
||||
if (!dockerComposeFileLocation.startsWith('/')) dockerComposeFileLocation = `/${dockerComposeFileLocation}`;
|
||||
if (dockerComposeFileLocation.endsWith('/')) dockerComposeFileLocation = dockerComposeFileLocation.slice(0, -1);
|
||||
if (!dockerComposeFileLocation.startsWith('/'))
|
||||
dockerComposeFileLocation = `/${dockerComposeFileLocation}`;
|
||||
if (dockerComposeFileLocation.endsWith('/'))
|
||||
dockerComposeFileLocation = dockerComposeFileLocation.slice(0, -1);
|
||||
} else {
|
||||
dockerComposeFileLocation = '/Dockerfile';
|
||||
}
|
||||
@ -479,7 +509,6 @@ export const scanningTemplates = {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const saveBuildLog = async ({
|
||||
line,
|
||||
buildId,
|
||||
@ -491,7 +520,7 @@ export const saveBuildLog = async ({
|
||||
}): Promise<any> => {
|
||||
if (buildId === 'undefined' || buildId === 'null' || !buildId) return;
|
||||
if (applicationId === 'undefined' || applicationId === 'null' || !applicationId) return;
|
||||
const { default: got } = await import('got')
|
||||
const { default: got } = await import('got');
|
||||
if (typeof line === 'object' && line) {
|
||||
if (line.shortMessage) {
|
||||
line = line.shortMessage + '\n' + line.stderr;
|
||||
@ -504,7 +533,11 @@ export const saveBuildLog = async ({
|
||||
line = line.replace(regex, '<SENSITIVE_DATA_DELETED>@');
|
||||
}
|
||||
const addTimestamp = `[${generateTimestamp()}] ${line}`;
|
||||
const fluentBitUrl = isDev ? process.env.COOLIFY_CONTAINER_DEV === 'true' ? 'http://coolify-fluentbit:24224' : 'http://localhost:24224' : 'http://coolify-fluentbit:24224';
|
||||
const fluentBitUrl = isDev
|
||||
? process.env.COOLIFY_CONTAINER_DEV === 'true'
|
||||
? 'http://coolify-fluentbit:24224'
|
||||
: 'http://localhost:24224'
|
||||
: 'http://coolify-fluentbit:24224';
|
||||
|
||||
if (isDev && !process.env.COOLIFY_CONTAINER_DEV) {
|
||||
console.debug(`[${applicationId}] ${addTimestamp}`);
|
||||
@ -514,15 +547,17 @@ export const saveBuildLog = async ({
|
||||
json: {
|
||||
line: encrypt(line)
|
||||
}
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
return await prisma.buildLog.create({
|
||||
data: {
|
||||
line: addTimestamp, buildId, time: Number(day().valueOf()), applicationId
|
||||
line: addTimestamp,
|
||||
buildId,
|
||||
time: Number(day().valueOf()),
|
||||
applicationId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export async function copyBaseConfigurationFiles(
|
||||
@ -610,7 +645,7 @@ export function checkPnpm(installCommand = null, buildCommand = null, startComma
|
||||
|
||||
export async function saveDockerRegistryCredentials({ url, username, password, workdir }) {
|
||||
if (!username || !password) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
let decryptedPassword = decrypt(password);
|
||||
@ -619,17 +654,17 @@ export async function saveDockerRegistryCredentials({ url, username, password, w
|
||||
try {
|
||||
await fs.mkdir(`${workdir}/.docker`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
// console.log(error);
|
||||
}
|
||||
const payload = JSON.stringify({
|
||||
"auths": {
|
||||
auths: {
|
||||
[url]: {
|
||||
"auth": Buffer.from(`${username}:${decryptedPassword}`).toString('base64')
|
||||
auth: Buffer.from(`${username}:${decryptedPassword}`).toString('base64')
|
||||
}
|
||||
}
|
||||
})
|
||||
await fs.writeFile(`${location}/config.json`, payload)
|
||||
return location
|
||||
});
|
||||
await fs.writeFile(`${location}/config.json`, payload);
|
||||
return location;
|
||||
}
|
||||
export async function buildImage({
|
||||
applicationId,
|
||||
@ -640,29 +675,41 @@ export async function buildImage({
|
||||
isCache = false,
|
||||
debug = false,
|
||||
dockerFileLocation = '/Dockerfile',
|
||||
commit
|
||||
commit,
|
||||
forceRebuild = false
|
||||
}) {
|
||||
if (isCache) {
|
||||
await saveBuildLog({ line: `Building cache image...`, buildId, applicationId });
|
||||
} else {
|
||||
await saveBuildLog({ line: `Building production image...`, buildId, applicationId });
|
||||
}
|
||||
const dockerFile = isCache ? `${dockerFileLocation}-cache` : `${dockerFileLocation}`
|
||||
const cache = `${applicationId}:${tag}${isCache ? '-cache' : ''}`
|
||||
const dockerFile = isCache ? `${dockerFileLocation}-cache` : `${dockerFileLocation}`;
|
||||
const cache = `${applicationId}:${tag}${isCache ? '-cache' : ''}`;
|
||||
let location = null;
|
||||
|
||||
let location = null
|
||||
|
||||
const { dockerRegistry } = await prisma.application.findUnique({ where: { id: applicationId }, select: { dockerRegistry: true } })
|
||||
const { dockerRegistry } = await prisma.application.findUnique({
|
||||
where: { id: applicationId },
|
||||
select: { dockerRegistry: true }
|
||||
});
|
||||
if (dockerRegistry) {
|
||||
const { url, username, password } = dockerRegistry
|
||||
location = await saveDockerRegistryCredentials({ url, username, password, workdir })
|
||||
const { url, username, password } = dockerRegistry;
|
||||
location = await saveDockerRegistryCredentials({ url, username, password, workdir });
|
||||
}
|
||||
|
||||
await executeCommand({ stream: true, debug, buildId, applicationId, dockerId, command: `docker ${location ? `--config ${location}` : ''} build --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}` })
|
||||
await executeCommand({
|
||||
stream: true,
|
||||
debug,
|
||||
buildId,
|
||||
applicationId,
|
||||
dockerId,
|
||||
command: `docker ${location ? `--config ${location}` : ''} build ${
|
||||
forceRebuild ? '--no-cache' : ''
|
||||
} --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}`
|
||||
});
|
||||
|
||||
const { status } = await prisma.build.findUnique({ where: { id: buildId } })
|
||||
const { status } = await prisma.build.findUnique({ where: { id: buildId } });
|
||||
if (status === 'canceled') {
|
||||
throw new Error('Canceled.')
|
||||
throw new Error('Canceled.');
|
||||
}
|
||||
}
|
||||
export function makeLabelForSimpleDockerfile({ applicationId, port, type }) {
|
||||
@ -741,21 +788,8 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (isPnpm) {
|
||||
@ -765,7 +799,6 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
if (installCommand) {
|
||||
Dockerfile.push(`RUN ${installCommand}`);
|
||||
}
|
||||
// Dockerfile.push(`ARG CACHEBUST=1`);
|
||||
Dockerfile.push(`RUN ${buildCommand}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
||||
await buildImage({ ...data, isCache: true });
|
||||
@ -773,27 +806,13 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
|
||||
export async function buildCacheImageForLaravel(data, imageForBuild) {
|
||||
const { workdir, buildId, secrets, pullmergeRequestId } = data;
|
||||
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${imageForBuild}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
Dockerfile.push(`COPY *.json *.mix.js /app/`);
|
||||
@ -804,11 +823,7 @@ export async function buildCacheImageForLaravel(data, imageForBuild) {
|
||||
}
|
||||
|
||||
export async function buildCacheImageWithCargo(data, imageForBuild) {
|
||||
const {
|
||||
applicationId,
|
||||
workdir,
|
||||
buildId,
|
||||
} = data;
|
||||
const { applicationId, workdir, buildId } = data;
|
||||
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${imageForBuild} as planner-${applicationId}`);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { defaultComposeConfiguration, executeCommand } from '../common';
|
||||
import { defaultComposeConfiguration, executeCommand, generateSecrets } from '../common';
|
||||
import { saveBuildLog } from './common';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
@ -25,30 +25,11 @@ export default async function (data) {
|
||||
if (!dockerComposeYaml.services) {
|
||||
throw 'No Services found in docker-compose file.';
|
||||
}
|
||||
const envs = [];
|
||||
let envs = [];
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter((s) => s.name === secret.name && s.isPRMRSecret);
|
||||
if (isSecretFound.length > 0) {
|
||||
envs.push(`${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
envs.push(`${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
envs.push(`${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
||||
let envFound = false;
|
||||
try {
|
||||
envFound = !!(await fs.stat(`${workdir}/.env`));
|
||||
} catch (error) {
|
||||
//
|
||||
envs = [...envs, ...generateSecrets(secrets, pullmergeRequestId, false, null)];
|
||||
}
|
||||
|
||||
const composeVolumes = [];
|
||||
if (volumes.length > 0) {
|
||||
for (const volume of volumes) {
|
||||
@ -62,7 +43,8 @@ export default async function (data) {
|
||||
let networks = {};
|
||||
for (let [key, value] of Object.entries(dockerComposeYaml.services)) {
|
||||
value['container_name'] = `${applicationId}-${key}`;
|
||||
value['env_file'] = envFound ? [`${workdir}/.env`] : [];
|
||||
let environment = typeof value['environment'] === 'undefined' ? [] : value['environment']
|
||||
value['environment'] = [...environment, ...envs];
|
||||
value['labels'] = labels;
|
||||
// TODO: If we support separated volume for each service, we need to add it here
|
||||
if (value['volumes']?.length > 0) {
|
||||
@ -106,6 +88,7 @@ export default async function (data) {
|
||||
dockerComposeYaml['volumes'] = { ...composeVolumes };
|
||||
}
|
||||
dockerComposeYaml['networks'] = Object.assign({ ...networks }, { [network]: { external: true } });
|
||||
|
||||
await fs.writeFile(fileYaml, yaml.dump(dockerComposeYaml));
|
||||
await executeCommand({
|
||||
debug,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@ -24,21 +25,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (depsFound) {
|
||||
|
@ -1,47 +1,27 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage } from './common';
|
||||
|
||||
export default async function (data) {
|
||||
let {
|
||||
applicationId,
|
||||
debug,
|
||||
tag,
|
||||
workdir,
|
||||
buildId,
|
||||
baseDirectory,
|
||||
secrets,
|
||||
pullmergeRequestId,
|
||||
dockerFileLocation
|
||||
} = data
|
||||
let { workdir, buildId, baseDirectory, secrets, pullmergeRequestId, dockerFileLocation } = data;
|
||||
const file = `${workdir}${baseDirectory}${dockerFileLocation}`;
|
||||
data.workdir = `${workdir}${baseDirectory}`;
|
||||
const DockerfileRaw = await fs.readFile(`${file}`, 'utf8')
|
||||
const Dockerfile: Array<string> = DockerfileRaw
|
||||
.toString()
|
||||
.trim()
|
||||
.split('\n');
|
||||
const DockerfileRaw = await fs.readFile(`${file}`, 'utf8');
|
||||
const Dockerfile: Array<string> = DockerfileRaw.toString().trim().split('\n');
|
||||
Dockerfile.forEach((line, index) => {
|
||||
if (line.startsWith('FROM')) {
|
||||
Dockerfile.splice(index + 1, 0, `LABEL coolify.buildId=${buildId}`);
|
||||
}
|
||||
});
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (
|
||||
(pullmergeRequestId && secret.isPRMRSecret) ||
|
||||
(!pullmergeRequestId && !secret.isPRMRSecret)
|
||||
) {
|
||||
Dockerfile.forEach((line, index) => {
|
||||
if (line.startsWith('FROM')) {
|
||||
Dockerfile.splice(index + 1, 0, `ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
});
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.forEach((line, index) => {
|
||||
if (line.startsWith('FROM')) {
|
||||
Dockerfile.splice(index + 1, 0, env);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
await fs.writeFile(`${workdir}${dockerFileLocation}`, Dockerfile.join('\n'));
|
||||
await fs.writeFile(`${data.workdir}${dockerFileLocation}`, Dockerfile.join('\n'));
|
||||
await buildImage(data);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@ -24,21 +25,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (isPnpm) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage, checkPnpm } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@ -20,21 +21,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (isPnpm) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@ -24,21 +25,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (isPnpm) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage } from './common';
|
||||
|
||||
const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
||||
@ -13,21 +14,8 @@ const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@ -18,21 +19,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildCacheImageWithNode, buildImage } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@ -25,21 +26,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
}
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (buildCommand) {
|
||||
|
@ -19,9 +19,10 @@ import { saveBuildLog, saveDockerRegistryCredentials } from './buildPacks/common
|
||||
import { scheduler } from './scheduler';
|
||||
import type { ExecaChildProcess } from 'execa';
|
||||
|
||||
export const version = '3.12.0';
|
||||
export const version = '3.12.9';
|
||||
export const isDev = process.env.NODE_ENV === 'development';
|
||||
export const sentryDSN = 'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216';
|
||||
export const sentryDSN =
|
||||
'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216';
|
||||
const algorithm = 'aes-256-ctr';
|
||||
const customConfig: Config = {
|
||||
dictionaries: [adjectives, colors, animals],
|
||||
@ -92,7 +93,7 @@ export const asyncExecShellStream = async ({
|
||||
line: `${line.replace('\n', '')}`,
|
||||
buildId,
|
||||
applicationId
|
||||
}
|
||||
};
|
||||
logs.push(log);
|
||||
if (debug) {
|
||||
await saveBuildLog(log);
|
||||
@ -109,7 +110,7 @@ export const asyncExecShellStream = async ({
|
||||
line: `${line.replace('\n', '')}`,
|
||||
buildId,
|
||||
applicationId
|
||||
}
|
||||
};
|
||||
logs.push(log);
|
||||
if (debug) {
|
||||
await saveBuildLog(log);
|
||||
@ -393,7 +394,6 @@ export function generateTimestamp(): string {
|
||||
return `${day().format('HH:mm:ss.SSS')}`;
|
||||
}
|
||||
|
||||
|
||||
export const supportedDatabaseTypesAndVersions = [
|
||||
{
|
||||
name: 'mongodb',
|
||||
@ -509,20 +509,19 @@ export async function createRemoteEngineConfiguration(id: string) {
|
||||
} = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } });
|
||||
await fs.writeFile(sshKeyFile, decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 });
|
||||
const config = sshConfig.parse('');
|
||||
const Host = `${remoteIpAddress}-remote`
|
||||
const Host = `${remoteIpAddress}-remote`;
|
||||
|
||||
try {
|
||||
await executeCommand({ command: `ssh-keygen -R ${Host}` });
|
||||
await executeCommand({ command: `ssh-keygen -R ${remoteIpAddress}` });
|
||||
await executeCommand({ command: `ssh-keygen -R localhost:${localPort}` });
|
||||
} catch (error) { }
|
||||
|
||||
} catch (error) {}
|
||||
|
||||
const found = config.find({ Host });
|
||||
const foundIp = config.find({ Host: remoteIpAddress });
|
||||
|
||||
if (found) config.remove({ Host })
|
||||
if (foundIp) config.remove({ Host: remoteIpAddress })
|
||||
if (found) config.remove({ Host });
|
||||
if (foundIp) config.remove({ Host: remoteIpAddress });
|
||||
|
||||
config.append({
|
||||
Host,
|
||||
@ -543,15 +542,35 @@ export async function createRemoteEngineConfiguration(id: string) {
|
||||
}
|
||||
return await fs.writeFile(`${homedir}/.ssh/config`, sshConfig.stringify(config));
|
||||
}
|
||||
export async function executeCommand({ command, dockerId = null, sshCommand = false, shell = false, stream = false, buildId, applicationId, debug }: { command: string, sshCommand?: boolean, shell?: boolean, stream?: boolean, dockerId?: string, buildId?: string, applicationId?: string, debug?: boolean }): Promise<ExecaChildProcess<string>> {
|
||||
const { execa, execaCommand } = await import('execa')
|
||||
const { parse } = await import('shell-quote')
|
||||
export async function executeCommand({
|
||||
command,
|
||||
dockerId = null,
|
||||
sshCommand = false,
|
||||
shell = false,
|
||||
stream = false,
|
||||
buildId,
|
||||
applicationId,
|
||||
debug
|
||||
}: {
|
||||
command: string;
|
||||
sshCommand?: boolean;
|
||||
shell?: boolean;
|
||||
stream?: boolean;
|
||||
dockerId?: string;
|
||||
buildId?: string;
|
||||
applicationId?: string;
|
||||
debug?: boolean;
|
||||
}): Promise<ExecaChildProcess<string>> {
|
||||
const { execa, execaCommand } = await import('execa');
|
||||
const { parse } = await import('shell-quote');
|
||||
const parsedCommand = parse(command);
|
||||
const dockerCommand = parsedCommand[0];
|
||||
const dockerArgs = parsedCommand.slice(1);
|
||||
|
||||
if (dockerId) {
|
||||
let { remoteEngine, remoteIpAddress, engine } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } })
|
||||
let { remoteEngine, remoteIpAddress, engine } = await prisma.destinationDocker.findUnique({
|
||||
where: { id: dockerId }
|
||||
});
|
||||
if (remoteEngine) {
|
||||
await createRemoteEngineConfiguration(dockerId);
|
||||
engine = `ssh://${remoteIpAddress}-remote`;
|
||||
@ -591,7 +610,7 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
|
||||
line: `${line.replace('\n', '')}`,
|
||||
buildId,
|
||||
applicationId
|
||||
}
|
||||
};
|
||||
logs.push(log);
|
||||
if (debug) {
|
||||
await saveBuildLog(log);
|
||||
@ -608,7 +627,7 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
|
||||
line: `${line.replace('\n', '')}`,
|
||||
buildId,
|
||||
applicationId
|
||||
}
|
||||
};
|
||||
logs.push(log);
|
||||
if (debug) {
|
||||
await saveBuildLog(log);
|
||||
@ -628,7 +647,7 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
|
||||
reject(code);
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
} else {
|
||||
if (shell) {
|
||||
return await execaCommand(command, {
|
||||
@ -640,7 +659,6 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (shell) {
|
||||
return execaCommand(command, { shell: true });
|
||||
@ -650,8 +668,13 @@ export async function executeCommand({ command, dockerId = null, sshCommand = fa
|
||||
}
|
||||
|
||||
export async function startTraefikProxy(id: string): Promise<void> {
|
||||
const { engine, network, remoteEngine, remoteIpAddress } = await prisma.destinationDocker.findUnique({ where: { id } })
|
||||
const { found } = await checkContainer({ dockerId: id, container: 'coolify-proxy', remove: true });
|
||||
const { engine, network, remoteEngine, remoteIpAddress } =
|
||||
await prisma.destinationDocker.findUnique({ where: { id } });
|
||||
const { found } = await checkContainer({
|
||||
dockerId: id,
|
||||
container: 'coolify-proxy',
|
||||
remove: true
|
||||
});
|
||||
const { id: settingsId, ipv4, ipv6 } = await listSettings();
|
||||
|
||||
if (!found) {
|
||||
@ -768,9 +791,12 @@ export async function listSettings(): Promise<any> {
|
||||
}
|
||||
|
||||
export function generateToken() {
|
||||
return jsonwebtoken.sign({
|
||||
nbf: Math.floor(Date.now() / 1000) - 30,
|
||||
}, process.env['COOLIFY_SECRET_KEY'])
|
||||
return jsonwebtoken.sign(
|
||||
{
|
||||
nbf: Math.floor(Date.now() / 1000) - 30
|
||||
},
|
||||
process.env['COOLIFY_SECRET_KEY']
|
||||
);
|
||||
}
|
||||
export function generatePassword({
|
||||
length = 24,
|
||||
@ -790,109 +816,102 @@ export function generatePassword({
|
||||
return password;
|
||||
}
|
||||
|
||||
type DatabaseConfiguration = {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MYSQL_DATABASE: string;
|
||||
MYSQL_PASSWORD: string;
|
||||
MYSQL_ROOT_USER: string;
|
||||
MYSQL_USER: string;
|
||||
MYSQL_ROOT_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
type DatabaseConfiguration =
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MONGO_INITDB_ROOT_USERNAME?: string;
|
||||
MONGO_INITDB_ROOT_PASSWORD?: string;
|
||||
MONGODB_ROOT_USER?: string;
|
||||
MONGODB_ROOT_PASSWORD?: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MYSQL_DATABASE: string;
|
||||
MYSQL_PASSWORD: string;
|
||||
MYSQL_ROOT_USER: string;
|
||||
MYSQL_USER: string;
|
||||
MYSQL_ROOT_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MARIADB_ROOT_USER: string;
|
||||
MARIADB_ROOT_PASSWORD: string;
|
||||
MARIADB_USER: string;
|
||||
MARIADB_PASSWORD: string;
|
||||
MARIADB_DATABASE: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MONGO_INITDB_ROOT_USERNAME?: string;
|
||||
MONGO_INITDB_ROOT_PASSWORD?: string;
|
||||
MONGODB_ROOT_USER?: string;
|
||||
MONGODB_ROOT_PASSWORD?: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
POSTGRES_PASSWORD?: string;
|
||||
POSTGRES_USER?: string;
|
||||
POSTGRES_DB?: string;
|
||||
POSTGRESQL_POSTGRES_PASSWORD?: string;
|
||||
POSTGRESQL_USERNAME?: string;
|
||||
POSTGRESQL_PASSWORD?: string;
|
||||
POSTGRESQL_DATABASE?: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MARIADB_ROOT_USER: string;
|
||||
MARIADB_ROOT_PASSWORD: string;
|
||||
MARIADB_USER: string;
|
||||
MARIADB_PASSWORD: string;
|
||||
MARIADB_DATABASE: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
REDIS_AOF_ENABLED: string;
|
||||
REDIS_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
POSTGRES_PASSWORD?: string;
|
||||
POSTGRES_USER?: string;
|
||||
POSTGRES_DB?: string;
|
||||
POSTGRESQL_POSTGRES_PASSWORD?: string;
|
||||
POSTGRESQL_USERNAME?: string;
|
||||
POSTGRESQL_PASSWORD?: string;
|
||||
POSTGRESQL_DATABASE?: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
COUCHDB_PASSWORD: string;
|
||||
COUCHDB_USER: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
REDIS_AOF_ENABLED: string;
|
||||
REDIS_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
EDGEDB_SERVER_PASSWORD: string;
|
||||
EDGEDB_SERVER_USER: string;
|
||||
EDGEDB_SERVER_DATABASE: string;
|
||||
EDGEDB_SERVER_TLS_CERT_MODE: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
COUCHDB_PASSWORD: string;
|
||||
COUCHDB_USER: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
EDGEDB_SERVER_PASSWORD: string;
|
||||
EDGEDB_SERVER_USER: string;
|
||||
EDGEDB_SERVER_DATABASE: string;
|
||||
EDGEDB_SERVER_TLS_CERT_MODE: string;
|
||||
};
|
||||
};
|
||||
export function generateDatabaseConfiguration(database: any, arch: string): DatabaseConfiguration {
|
||||
const {
|
||||
id,
|
||||
dbUser,
|
||||
dbUserPassword,
|
||||
rootUser,
|
||||
rootUserPassword,
|
||||
defaultDatabase,
|
||||
version,
|
||||
type,
|
||||
} = database;
|
||||
const { id, dbUser, dbUserPassword, rootUser, rootUserPassword, defaultDatabase, version, type } =
|
||||
database;
|
||||
const baseImage = getDatabaseImage(type, arch);
|
||||
if (type === 'mysql') {
|
||||
const configuration = {
|
||||
@ -972,7 +991,9 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
}
|
||||
return configuration;
|
||||
} else if (type === 'redis') {
|
||||
const { settings: { appendOnly } } = database;
|
||||
const {
|
||||
settings: { appendOnly }
|
||||
} = database;
|
||||
const configuration: DatabaseConfiguration = {
|
||||
privatePort: 6379,
|
||||
command: undefined,
|
||||
@ -986,8 +1007,9 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
};
|
||||
if (isARM(arch)) {
|
||||
configuration.volume = `${id}-${type}-data:/data`;
|
||||
configuration.command = `/usr/local/bin/redis-server --appendonly ${appendOnly ? 'yes' : 'no'
|
||||
} --requirepass ${dbUserPassword}`;
|
||||
configuration.command = `/usr/local/bin/redis-server --appendonly ${
|
||||
appendOnly ? 'yes' : 'no'
|
||||
} --requirepass ${dbUserPassword}`;
|
||||
}
|
||||
return configuration;
|
||||
} else if (type === 'couchdb') {
|
||||
@ -1004,7 +1026,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
if (isARM(arch)) {
|
||||
configuration.volume = `${id}-${type}-data:/opt/couchdb/data`;
|
||||
}
|
||||
return configuration
|
||||
return configuration;
|
||||
} else if (type === 'edgedb') {
|
||||
const configuration: DatabaseConfiguration = {
|
||||
privatePort: 5656,
|
||||
@ -1018,7 +1040,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
volume: `${id}-${type}-data:/var/lib/edgedb/data`,
|
||||
ulimits: {}
|
||||
};
|
||||
return configuration
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
export function isARM(arch: string) {
|
||||
@ -1071,12 +1093,12 @@ export type ComposeFileService = {
|
||||
command?: string;
|
||||
ports?: string[];
|
||||
build?:
|
||||
| {
|
||||
context: string;
|
||||
dockerfile: string;
|
||||
args?: Record<string, unknown>;
|
||||
}
|
||||
| string;
|
||||
| {
|
||||
context: string;
|
||||
dockerfile: string;
|
||||
args?: Record<string, unknown>;
|
||||
}
|
||||
| string;
|
||||
deploy?: {
|
||||
restart_policy?: {
|
||||
condition?: string;
|
||||
@ -1141,13 +1163,13 @@ export const createDirectories = async ({
|
||||
repository: string;
|
||||
buildId: string;
|
||||
}): Promise<{ workdir: string; repodir: string }> => {
|
||||
if (repository) repository = repository.replaceAll(' ', '')
|
||||
if (repository) repository = repository.replaceAll(' ', '');
|
||||
const repodir = `/tmp/build-sources/${repository}/`;
|
||||
const workdir = `/tmp/build-sources/${repository}/${buildId}`;
|
||||
let workdirFound = false;
|
||||
try {
|
||||
workdirFound = !!(await fs.stat(workdir));
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
if (workdirFound) {
|
||||
await executeCommand({ command: `rm -fr ${workdir}` });
|
||||
}
|
||||
@ -1254,19 +1276,45 @@ export async function updatePasswordInDb(database, user, newPassword, isRoot) {
|
||||
}
|
||||
}
|
||||
}
|
||||
export async function checkExposedPort({ id, configuredPort, exposePort, engine, remoteEngine, remoteIpAddress }: { id: string, configuredPort?: number, exposePort: number, engine: string, remoteEngine: boolean, remoteIpAddress?: string }) {
|
||||
export async function checkExposedPort({
|
||||
id,
|
||||
configuredPort,
|
||||
exposePort,
|
||||
engine,
|
||||
remoteEngine,
|
||||
remoteIpAddress
|
||||
}: {
|
||||
id: string;
|
||||
configuredPort?: number;
|
||||
exposePort: number;
|
||||
engine: string;
|
||||
remoteEngine: boolean;
|
||||
remoteIpAddress?: string;
|
||||
}) {
|
||||
if (exposePort < 1024 || exposePort > 65535) {
|
||||
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` };
|
||||
}
|
||||
if (configuredPort) {
|
||||
if (configuredPort !== exposePort) {
|
||||
const availablePort = await getFreeExposedPort(id, exposePort, engine, remoteEngine, remoteIpAddress);
|
||||
const availablePort = await getFreeExposedPort(
|
||||
id,
|
||||
exposePort,
|
||||
engine,
|
||||
remoteEngine,
|
||||
remoteIpAddress
|
||||
);
|
||||
if (availablePort.toString() !== exposePort.toString()) {
|
||||
throw { status: 500, message: `Port ${exposePort} is already in use.` };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const availablePort = await getFreeExposedPort(id, exposePort, engine, remoteEngine, remoteIpAddress);
|
||||
const availablePort = await getFreeExposedPort(
|
||||
id,
|
||||
exposePort,
|
||||
engine,
|
||||
remoteEngine,
|
||||
remoteIpAddress
|
||||
);
|
||||
if (availablePort.toString() !== exposePort.toString()) {
|
||||
throw { status: 500, message: `Port ${exposePort} is already in use.` };
|
||||
}
|
||||
@ -1277,25 +1325,33 @@ export async function getFreeExposedPort(id, exposePort, engine, remoteEngine, r
|
||||
if (remoteEngine) {
|
||||
const applicationUsed = await (
|
||||
await prisma.application.findMany({
|
||||
where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { remoteIpAddress } },
|
||||
where: {
|
||||
exposePort: { not: null },
|
||||
id: { not: id },
|
||||
destinationDocker: { remoteIpAddress }
|
||||
},
|
||||
select: { exposePort: true }
|
||||
})
|
||||
).map((a) => a.exposePort);
|
||||
const serviceUsed = await (
|
||||
await prisma.service.findMany({
|
||||
where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { remoteIpAddress } },
|
||||
where: {
|
||||
exposePort: { not: null },
|
||||
id: { not: id },
|
||||
destinationDocker: { remoteIpAddress }
|
||||
},
|
||||
select: { exposePort: true }
|
||||
})
|
||||
).map((a) => a.exposePort);
|
||||
const usedPorts = [...applicationUsed, ...serviceUsed];
|
||||
if (usedPorts.includes(exposePort)) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
const found = await checkPort(exposePort, { host: remoteIpAddress });
|
||||
if (!found) {
|
||||
return exposePort
|
||||
return exposePort;
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
} else {
|
||||
const applicationUsed = await (
|
||||
await prisma.application.findMany({
|
||||
@ -1311,13 +1367,13 @@ export async function getFreeExposedPort(id, exposePort, engine, remoteEngine, r
|
||||
).map((a) => a.exposePort);
|
||||
const usedPorts = [...applicationUsed, ...serviceUsed];
|
||||
if (usedPorts.includes(exposePort)) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
const found = await checkPort(exposePort, { host: 'localhost' });
|
||||
if (!found) {
|
||||
return exposePort
|
||||
return exposePort;
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function generateRangeArray(start, end) {
|
||||
@ -1330,38 +1386,54 @@ export async function getFreePublicPort({ id, remoteEngine, engine, remoteIpAddr
|
||||
if (remoteEngine) {
|
||||
const dbUsed = await (
|
||||
await prisma.database.findMany({
|
||||
where: { publicPort: { not: null }, id: { not: id }, destinationDocker: { remoteIpAddress } },
|
||||
where: {
|
||||
publicPort: { not: null },
|
||||
id: { not: id },
|
||||
destinationDocker: { remoteIpAddress }
|
||||
},
|
||||
select: { publicPort: true }
|
||||
})
|
||||
).map((a) => a.publicPort);
|
||||
const wpFtpUsed = await (
|
||||
await prisma.wordpress.findMany({
|
||||
where: { ftpPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { remoteIpAddress } } },
|
||||
where: {
|
||||
ftpPublicPort: { not: null },
|
||||
id: { not: id },
|
||||
service: { destinationDocker: { remoteIpAddress } }
|
||||
},
|
||||
select: { ftpPublicPort: true }
|
||||
})
|
||||
).map((a) => a.ftpPublicPort);
|
||||
const wpUsed = await (
|
||||
await prisma.wordpress.findMany({
|
||||
where: { mysqlPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { remoteIpAddress } } },
|
||||
where: {
|
||||
mysqlPublicPort: { not: null },
|
||||
id: { not: id },
|
||||
service: { destinationDocker: { remoteIpAddress } }
|
||||
},
|
||||
select: { mysqlPublicPort: true }
|
||||
})
|
||||
).map((a) => a.mysqlPublicPort);
|
||||
const minioUsed = await (
|
||||
await prisma.minio.findMany({
|
||||
where: { publicPort: { not: null }, id: { not: id }, service: { destinationDocker: { remoteIpAddress } } },
|
||||
where: {
|
||||
publicPort: { not: null },
|
||||
id: { not: id },
|
||||
service: { destinationDocker: { remoteIpAddress } }
|
||||
},
|
||||
select: { publicPort: true }
|
||||
})
|
||||
).map((a) => a.publicPort);
|
||||
const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed];
|
||||
const range = generateRangeArray(minPort, maxPort)
|
||||
const availablePorts = range.filter(port => !usedPorts.includes(port))
|
||||
const range = generateRangeArray(minPort, maxPort);
|
||||
const availablePorts = range.filter((port) => !usedPorts.includes(port));
|
||||
for (const port of availablePorts) {
|
||||
const found = await isReachable(port, { host: remoteIpAddress })
|
||||
const found = await isReachable(port, { host: remoteIpAddress });
|
||||
if (!found) {
|
||||
return port
|
||||
return port;
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
} else {
|
||||
const dbUsed = await (
|
||||
await prisma.database.findMany({
|
||||
@ -1371,32 +1443,44 @@ export async function getFreePublicPort({ id, remoteEngine, engine, remoteIpAddr
|
||||
).map((a) => a.publicPort);
|
||||
const wpFtpUsed = await (
|
||||
await prisma.wordpress.findMany({
|
||||
where: { ftpPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { engine } } },
|
||||
where: {
|
||||
ftpPublicPort: { not: null },
|
||||
id: { not: id },
|
||||
service: { destinationDocker: { engine } }
|
||||
},
|
||||
select: { ftpPublicPort: true }
|
||||
})
|
||||
).map((a) => a.ftpPublicPort);
|
||||
const wpUsed = await (
|
||||
await prisma.wordpress.findMany({
|
||||
where: { mysqlPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { engine } } },
|
||||
where: {
|
||||
mysqlPublicPort: { not: null },
|
||||
id: { not: id },
|
||||
service: { destinationDocker: { engine } }
|
||||
},
|
||||
select: { mysqlPublicPort: true }
|
||||
})
|
||||
).map((a) => a.mysqlPublicPort);
|
||||
const minioUsed = await (
|
||||
await prisma.minio.findMany({
|
||||
where: { publicPort: { not: null }, id: { not: id }, service: { destinationDocker: { engine } } },
|
||||
where: {
|
||||
publicPort: { not: null },
|
||||
id: { not: id },
|
||||
service: { destinationDocker: { engine } }
|
||||
},
|
||||
select: { publicPort: true }
|
||||
})
|
||||
).map((a) => a.publicPort);
|
||||
const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed];
|
||||
const range = generateRangeArray(minPort, maxPort)
|
||||
const availablePorts = range.filter(port => !usedPorts.includes(port))
|
||||
const range = generateRangeArray(minPort, maxPort);
|
||||
const availablePorts = range.filter((port) => !usedPorts.includes(port));
|
||||
for (const port of availablePorts) {
|
||||
const found = await isReachable(port, { host: 'localhost' })
|
||||
const found = await isReachable(port, { host: 'localhost' });
|
||||
if (!found) {
|
||||
return port
|
||||
return port;
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1502,11 +1586,11 @@ export async function getServiceFromDB({
|
||||
serviceSecret: true,
|
||||
serviceSetting: true,
|
||||
wordpress: true,
|
||||
plausibleAnalytics: true,
|
||||
plausibleAnalytics: true
|
||||
}
|
||||
});
|
||||
if (!body) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
// body.type = fixType(body.type);
|
||||
|
||||
@ -1523,7 +1607,6 @@ export async function getServiceFromDB({
|
||||
return { ...body, settings };
|
||||
}
|
||||
|
||||
|
||||
export function fixType(type) {
|
||||
return type?.replaceAll(' ', '').toLowerCase() || null;
|
||||
}
|
||||
@ -1610,7 +1693,7 @@ export async function stopBuild(buildId, applicationId) {
|
||||
}
|
||||
}
|
||||
count++;
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
@ -1640,16 +1723,22 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
|
||||
|
||||
images = images.trim();
|
||||
if (images) {
|
||||
await executeCommand({ dockerId, command: `docker rmi -f ${images}" -q | xargs -r`, shell: true });
|
||||
await executeCommand({
|
||||
dockerId,
|
||||
command: `docker rmi -f ${images}" -q | xargs -r`,
|
||||
shell: true
|
||||
});
|
||||
}
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
if (lowDiskSpace || force) {
|
||||
// Cleanup images that are not used
|
||||
try {
|
||||
await executeCommand({ dockerId, command: `docker image prune -f` });
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
|
||||
const { numberOfDockerImagesKeptLocally } = await prisma.setting.findUnique({ where: { id: '0' } })
|
||||
const { numberOfDockerImagesKeptLocally } = await prisma.setting.findUnique({
|
||||
where: { id: '0' }
|
||||
});
|
||||
const { stdout: images } = await executeCommand({
|
||||
dockerId,
|
||||
command: `docker images|grep -v "<none>"|grep -v REPOSITORY|awk '{print $1, $2}'`,
|
||||
@ -1657,22 +1746,29 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
|
||||
});
|
||||
const imagesArray = images.trim().replaceAll(' ', ':').split('\n');
|
||||
const imagesSet = new Set(imagesArray.map((image) => image.split(':')[0]));
|
||||
let deleteImage = []
|
||||
let deleteImage = [];
|
||||
for (const image of imagesSet) {
|
||||
let keepImage = []
|
||||
let keepImage = [];
|
||||
for (const image2 of imagesArray) {
|
||||
if (image2.startsWith(image)) {
|
||||
if (force) {
|
||||
deleteImage.push(image2);
|
||||
continue;
|
||||
}
|
||||
if (keepImage.length >= numberOfDockerImagesKeptLocally) {
|
||||
deleteImage.push(image2)
|
||||
deleteImage.push(image2);
|
||||
} else {
|
||||
keepImage.push(image2)
|
||||
keepImage.push(image2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
for (const image of deleteImage) {
|
||||
await executeCommand({ dockerId, command: `docker image rm -f ${image}` });
|
||||
try {
|
||||
await executeCommand({ dockerId, command: `docker image rm -f ${image}` });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Prune coolify managed containers
|
||||
@ -1681,12 +1777,12 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
|
||||
dockerId,
|
||||
command: `docker container prune -f --filter "label=coolify.managed=true"`
|
||||
});
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
|
||||
// Cleanup build caches
|
||||
try {
|
||||
await executeCommand({ dockerId, command: `docker builder prune -a -f` });
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1768,16 +1864,90 @@ export function decryptApplication(application: any) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function pushToRegistry(application: any, workdir: string, tag: string, imageName: string, customTag: string) {
|
||||
const location = `${workdir}/.docker`
|
||||
const tagCommand = `docker tag ${application.id}:${tag} ${imageName}:${customTag}`
|
||||
const pushCommand = `docker --config ${location} push ${imageName}:${customTag}`
|
||||
export async function pushToRegistry(
|
||||
application: any,
|
||||
workdir: string,
|
||||
tag: string,
|
||||
imageName: string,
|
||||
customTag: string
|
||||
) {
|
||||
const location = `${workdir}/.docker`;
|
||||
const tagCommand = `docker tag ${application.id}:${tag} ${imageName}:${customTag}`;
|
||||
const pushCommand = `docker --config ${location} push ${imageName}:${customTag}`;
|
||||
await executeCommand({
|
||||
dockerId: application.destinationDockerId,
|
||||
command: tagCommand
|
||||
})
|
||||
});
|
||||
await executeCommand({
|
||||
dockerId: application.destinationDockerId,
|
||||
command: pushCommand
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function generateSecrets(
|
||||
secrets: Array<any>,
|
||||
pullmergeRequestId: string,
|
||||
isBuild = false,
|
||||
port = null
|
||||
): Array<string> {
|
||||
const envs = [];
|
||||
const isPRMRSecret = secrets.filter((s) => s.isPRMRSecret);
|
||||
const normalSecrets = secrets.filter((s) => !s.isPRMRSecret);
|
||||
if (pullmergeRequestId && isPRMRSecret.length > 0) {
|
||||
isPRMRSecret.forEach((secret) => {
|
||||
if (isBuild && !secret.isBuildSecret) {
|
||||
return;
|
||||
}
|
||||
const build = isBuild && secret.isBuildSecret;
|
||||
if (secret.value.includes('$')) {
|
||||
secret.value = secret.value.replaceAll('$', '$$$$');
|
||||
}
|
||||
if (secret.value.includes(' ') || secret.value.includes('\\n')) {
|
||||
if (build) {
|
||||
envs.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
} else {
|
||||
envs.push(`${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (build) {
|
||||
envs.push(`ARG ${secret.name}=${secret.value}`);
|
||||
} else {
|
||||
envs.push(`${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!pullmergeRequestId && normalSecrets.length > 0) {
|
||||
normalSecrets.forEach((secret) => {
|
||||
if (isBuild && !secret.isBuildSecret) {
|
||||
return;
|
||||
}
|
||||
if (secret.value.includes('$')) {
|
||||
secret.value = secret.value.replaceAll('$', '$$$$');
|
||||
}
|
||||
const build = isBuild && secret.isBuildSecret;
|
||||
if (secret.value.includes(' ') || secret.value.includes('\\n')) {
|
||||
if (build) {
|
||||
envs.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
} else {
|
||||
envs.push(`${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (build) {
|
||||
envs.push(`ARG ${secret.name}=${secret.value}`);
|
||||
} else {
|
||||
envs.push(`${secret.name}=${secret.value}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const portFound = envs.filter((env) => env.startsWith('PORT'));
|
||||
if (portFound.length === 0 && port && !isBuild) {
|
||||
envs.push(`PORT=${port}`);
|
||||
}
|
||||
const nodeEnv = envs.filter((env) => env.startsWith('NODE_ENV'));
|
||||
if (nodeEnv.length === 0 && !isBuild) {
|
||||
envs.push(`NODE_ENV=production`);
|
||||
}
|
||||
return envs;
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ export default async function ({
|
||||
buildId,
|
||||
privateSshKey,
|
||||
customPort,
|
||||
forPublic
|
||||
forPublic,
|
||||
customUser,
|
||||
}: {
|
||||
applicationId: string;
|
||||
workdir: string;
|
||||
@ -25,6 +26,7 @@ export default async function ({
|
||||
privateSshKey: string;
|
||||
customPort: number;
|
||||
forPublic: boolean;
|
||||
customUser: string;
|
||||
}): Promise<string> {
|
||||
const url = htmlUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
|
||||
if (!forPublic) {
|
||||
@ -53,7 +55,7 @@ export default async function ({
|
||||
} else {
|
||||
await executeCommand({
|
||||
command:
|
||||
`git clone -q -b ${branch} git@${url}:${repository}.git --config core.sshCommand="ssh -p ${customPort} -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, shell: true
|
||||
`git clone -q -b ${branch} ${customUser}@${url}:${repository}.git --config core.sshCommand="ssh -p ${customPort} -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, shell: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,51 +1,47 @@
|
||||
import { isARM, isDev } from "./common";
|
||||
import { isARM, isDev } from './common';
|
||||
import fs from 'fs/promises';
|
||||
export async function getTemplates() {
|
||||
const templatePath = isDev ? './templates.json' : '/app/templates.json';
|
||||
const open = await fs.open(templatePath, 'r');
|
||||
try {
|
||||
let data = await open.readFile({ encoding: 'utf-8' });
|
||||
let jsonData = JSON.parse(data)
|
||||
if (isARM(process.arch)) {
|
||||
jsonData = jsonData.filter(d => d.arch !== 'amd64')
|
||||
}
|
||||
return jsonData;
|
||||
} catch (error) {
|
||||
return []
|
||||
} finally {
|
||||
await open?.close()
|
||||
}
|
||||
const templatePath = isDev ? './templates.json' : '/app/templates.json';
|
||||
const open = await fs.open(templatePath, 'r');
|
||||
try {
|
||||
let data = await open.readFile({ encoding: 'utf-8' });
|
||||
let jsonData = JSON.parse(data);
|
||||
if (isARM(process.arch)) {
|
||||
jsonData = jsonData.filter((d) => d.arch !== 'amd64');
|
||||
}
|
||||
return jsonData;
|
||||
} catch (error) {
|
||||
return [];
|
||||
} finally {
|
||||
await open?.close();
|
||||
}
|
||||
}
|
||||
const compareSemanticVersions = (a: string, b: string) => {
|
||||
const a1 = a.split('.');
|
||||
const b1 = b.split('.');
|
||||
const len = Math.min(a1.length, b1.length);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const a2 = +a1[i] || 0;
|
||||
const b2 = +b1[i] || 0;
|
||||
if (a2 !== b2) {
|
||||
return a2 > b2 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return b1.length - a1.length;
|
||||
const a1 = a.split('.');
|
||||
const b1 = b.split('.');
|
||||
const len = Math.min(a1.length, b1.length);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const a2 = +a1[i] || 0;
|
||||
const b2 = +b1[i] || 0;
|
||||
if (a2 !== b2) {
|
||||
return a2 > b2 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return b1.length - a1.length;
|
||||
};
|
||||
export async function getTags(type: string) {
|
||||
|
||||
try {
|
||||
if (type) {
|
||||
const tagsPath = isDev ? './tags.json' : '/app/tags.json';
|
||||
const data = await fs.readFile(tagsPath, 'utf8')
|
||||
let tags = JSON.parse(data)
|
||||
if (tags) {
|
||||
tags = tags.find((tag: any) => tag.name.includes(type))
|
||||
tags.tags = tags.tags.sort(compareSemanticVersions).reverse();
|
||||
return tags
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return []
|
||||
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if (type) {
|
||||
const tagsPath = isDev ? './tags.json' : '/app/tags.json';
|
||||
const data = await fs.readFile(tagsPath, 'utf8');
|
||||
let tags = JSON.parse(data);
|
||||
if (tags) {
|
||||
tags = tags.find((tag: any) => tag.name.includes(type));
|
||||
tags.tags = tags.tags.sort(compareSemanticVersions).reverse();
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,31 +1,31 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { errorHandler, listSettings, version } from '../../../../lib/common';
|
||||
import { errorHandler, isARM, listSettings, version } from '../../../../lib/common';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
try {
|
||||
await request.jwtVerify()
|
||||
} catch(error) {
|
||||
return
|
||||
}
|
||||
});
|
||||
fastify.get('/', async (request) => {
|
||||
const teamId = request.user?.teamId;
|
||||
const settings = await listSettings()
|
||||
try {
|
||||
return {
|
||||
ipv4: teamId ? settings.ipv4 : null,
|
||||
ipv6: teamId ? settings.ipv6 : null,
|
||||
version,
|
||||
whiteLabeled: process.env.COOLIFY_WHITE_LABELED === 'true',
|
||||
whiteLabeledIcon: process.env.COOLIFY_WHITE_LABELED_ICON,
|
||||
isRegistrationEnabled: settings.isRegistrationEnabled,
|
||||
}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
});
|
||||
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
try {
|
||||
await request.jwtVerify();
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
fastify.get('/', async (request) => {
|
||||
const teamId = request.user?.teamId;
|
||||
const settings = await listSettings();
|
||||
try {
|
||||
return {
|
||||
ipv4: teamId ? settings.ipv4 : null,
|
||||
ipv6: teamId ? settings.ipv6 : null,
|
||||
version,
|
||||
whiteLabeled: process.env.COOLIFY_WHITE_LABELED === 'true',
|
||||
whiteLabeledIcon: process.env.COOLIFY_WHITE_LABELED_ICON,
|
||||
isRegistrationEnabled: settings.isRegistrationEnabled,
|
||||
isARM: isARM(process.arch)
|
||||
};
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
@ -263,14 +263,15 @@ export async function parseAndFindServiceTemplates(service: any, workdir?: strin
|
||||
for (const secret of service.serviceSecret) {
|
||||
let { name, value } = secret
|
||||
name = name.toLowerCase()
|
||||
const regexHashed = new RegExp(`\\$\\$hashed\\$\\$secret_${name}\"`, 'gi')
|
||||
const regex = new RegExp(`\\$\\$secret_${name}\"`, 'gi')
|
||||
const regexHashed = new RegExp(`\\$\\$hashed\\$\\$secret_${name}`, 'gi')
|
||||
const regex = new RegExp(`\\$\\$secret_${name}`, 'gi')
|
||||
console.log({value, name, regex, regexHashed})
|
||||
if (value) {
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regexHashed, bcrypt.hashSync(value.replaceAll("\"", "\\\""), 10) + '"')
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regex, value.replaceAll("\"", "\\\"") + '"')
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regexHashed, bcrypt.hashSync(value.replaceAll("\"", "\\\""), 10))
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regex, value.replaceAll("\"", "\\\""))
|
||||
} else {
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regexHashed, '' + '"')
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regex, '' + '"')
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regexHashed, '')
|
||||
strParsedTemplate = strParsedTemplate.replaceAll(regex, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,11 +22,11 @@ export async function listSources(request: FastifyRequest) {
|
||||
export async function saveSource(request, reply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let { name, htmlUrl, apiUrl, customPort, isSystemWide } = request.body
|
||||
let { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide } = request.body
|
||||
if (customPort) customPort = Number(customPort)
|
||||
await prisma.gitSource.update({
|
||||
where: { id },
|
||||
data: { name, htmlUrl, apiUrl, customPort, isSystemWide }
|
||||
data: { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide }
|
||||
});
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
@ -48,6 +48,7 @@ export async function getSource(request: FastifyRequest<OnlyId>) {
|
||||
apiUrl: null,
|
||||
organization: null,
|
||||
customPort: 22,
|
||||
customUser: 'git',
|
||||
},
|
||||
settings
|
||||
}
|
||||
@ -133,7 +134,7 @@ export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { teamId } = request.user
|
||||
let { type, name, htmlUrl, apiUrl, oauthId, appId, appSecret, groupName, customPort } =
|
||||
let { type, name, htmlUrl, apiUrl, oauthId, appId, appSecret, groupName, customPort, customUser } =
|
||||
request.body
|
||||
|
||||
if (oauthId) oauthId = Number(oauthId);
|
||||
@ -142,7 +143,7 @@ export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>
|
||||
|
||||
if (id === 'new') {
|
||||
const newId = cuid()
|
||||
await prisma.gitSource.create({ data: { id: newId, type, apiUrl, htmlUrl, name, customPort, teams: { connect: { id: teamId } } } });
|
||||
await prisma.gitSource.create({ data: { id: newId, type, apiUrl, htmlUrl, name, customPort, customUser, teams: { connect: { id: teamId } } } });
|
||||
await prisma.gitlabApp.create({
|
||||
data: {
|
||||
teams: { connect: { id: teamId } },
|
||||
@ -158,7 +159,7 @@ export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>
|
||||
id: newId
|
||||
}
|
||||
} else {
|
||||
await prisma.gitSource.update({ where: { id }, data: { type, apiUrl, htmlUrl, name, customPort } });
|
||||
await prisma.gitSource.update({ where: { id }, data: { type, apiUrl, htmlUrl, name, customPort, customUser } });
|
||||
await prisma.gitlabApp.update({
|
||||
where: { id },
|
||||
data: {
|
||||
|
@ -21,6 +21,7 @@ export interface SaveGitLabSource extends OnlyId {
|
||||
appSecret: string,
|
||||
groupName: string,
|
||||
customPort: number,
|
||||
customUser: string,
|
||||
}
|
||||
}
|
||||
export interface CheckGitLabOAuthId extends OnlyId {
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
13
apps/client/.eslintignore
Normal file
13
apps/client/.eslintignore
Normal file
@ -0,0 +1,13 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
20
apps/client/.eslintrc.cjs
Normal file
20
apps/client/.eslintrc.cjs
Normal file
@ -0,0 +1,20 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
|
||||
plugins: ['svelte3', '@typescript-eslint'],
|
||||
ignorePatterns: ['*.cjs'],
|
||||
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
||||
settings: {
|
||||
'svelte3/typescript': () => require('typescript')
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
}
|
||||
};
|
10
apps/client/.gitignore
vendored
Normal file
10
apps/client/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
1
apps/client/.npmrc
Normal file
1
apps/client/.npmrc
Normal file
@ -0,0 +1 @@
|
||||
engine-strict=true
|
13
apps/client/.prettierignore
Normal file
13
apps/client/.prettierignore
Normal file
@ -0,0 +1,13 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
9
apps/client/.prettierrc
Normal file
9
apps/client/.prettierrc
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"pluginSearchDirs": ["."],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
1
apps/client/README.md
Normal file
1
apps/client/README.md
Normal file
@ -0,0 +1 @@
|
||||
# SvelteKit Static site
|
54
apps/client/package.json
Normal file
54
apps/client/package.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "client",
|
||||
"description": "Coolify's SvelteKit UI",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build && cp -Pr build/ ../../build/public",
|
||||
"preview": "vite preview",
|
||||
"test": "playwright test",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||
"format": "prettier --plugin-search-dir . --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.28.1",
|
||||
"@sveltejs/adapter-static": "1.0.0-next.48",
|
||||
"@sveltejs/kit": "1.0.0-next.572",
|
||||
"@types/js-cookie": "3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.44.0",
|
||||
"@typescript-eslint/parser": "5.44.0",
|
||||
"autoprefixer": "10.4.13",
|
||||
"eslint": "8.28.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-svelte3": "4.0.0",
|
||||
"postcss": "8.4.19",
|
||||
"postcss-load-config": "4.0.1",
|
||||
"prettier": "2.8.0",
|
||||
"prettier-plugin-svelte": "2.8.1",
|
||||
"svelte": "3.53.1",
|
||||
"svelte-check": "2.9.2",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"tailwindcss": "3.2.4",
|
||||
"tslib": "2.4.1",
|
||||
"typescript": "4.9.3",
|
||||
"vite": "3.2.4"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@trpc/client": "10.1.0",
|
||||
"@trpc/server": "10.1.0",
|
||||
"cuid": "2.1.8",
|
||||
"daisyui": "2.41.0",
|
||||
"dayjs": "1.11.6",
|
||||
"flowbite-svelte": "0.28.0",
|
||||
"js-cookie": "3.0.1",
|
||||
"js-yaml": "4.1.0",
|
||||
"p-limit": "4.0.0",
|
||||
"server": "workspace:*",
|
||||
"superjson": "1.11.0",
|
||||
"svelte-select": "4.4.7"
|
||||
}
|
||||
}
|
10
apps/client/playwright.config.ts
Normal file
10
apps/client/playwright.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
webServer: {
|
||||
command: 'npm run build && npm run preview',
|
||||
port: 4173
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
1793
apps/client/pnpm-lock.yaml
generated
Normal file
1793
apps/client/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
apps/client/postcss.config.cjs
Normal file
13
apps/client/postcss.config.cjs
Normal file
@ -0,0 +1,13 @@
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
|
||||
const config = {
|
||||
plugins: [
|
||||
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
|
||||
tailwindcss(),
|
||||
//But others, like autoprefixer, need to run after,
|
||||
autoprefixer
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = config;
|
12
apps/client/src/app.d.ts
vendored
Normal file
12
apps/client/src/app.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
// and what to do when importing types
|
||||
declare namespace App {
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface Error {}
|
||||
// interface Platform {}
|
||||
}
|
||||
|
||||
declare const GITPOD_WORKSPACE_URL: string;
|
||||
declare const CODESANDBOX_HOST: string;
|
12
apps/client/src/app.html
Normal file
12
apps/client/src/app.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body>
|
||||
<div class="h-screen">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
284
apps/client/src/app.postcss
Normal file
284
apps/client/src/app.postcss
Normal file
@ -0,0 +1,284 @@
|
||||
/* Write your global styles here, in PostCSS syntax */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(''), url('/poppins-v19-latin-ext_latin_devanagari-regular.woff2') format('woff2'),
|
||||
url('/poppins-v19-latin-ext_latin_devanagari-regular.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local(''), url('/poppins-v19-latin-ext_latin_devanagari-500.woff2') format('woff2'),
|
||||
url('/poppins-v19-latin-ext_latin_devanagari-500.woff') format('woff');
|
||||
}
|
||||
|
||||
button {
|
||||
@apply text-sm !important;
|
||||
}
|
||||
html {
|
||||
@apply h-full min-h-full overflow-y-scroll;
|
||||
}
|
||||
body {
|
||||
@apply min-h-screen overflow-x-hidden bg-coolblack text-sm text-white scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200;
|
||||
}
|
||||
|
||||
input,
|
||||
.input {
|
||||
@apply h-12 w-96 rounded border border-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-200 disabled:bg-transparent disabled:bg-coolblack md:text-sm;
|
||||
}
|
||||
textarea {
|
||||
@apply min-w-[14rem] rounded border border-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-200 disabled:bg-transparent md:text-sm;
|
||||
}
|
||||
|
||||
#svelte .custom-select-wrapper .selectContainer.disabled input {
|
||||
@apply placeholder:text-stone-600;
|
||||
}
|
||||
|
||||
#svelte .custom-select-wrapper .selectContainer input {
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
#svelte .custom-select-wrapper .selectContainer {
|
||||
@apply h-12 rounded bg-coolgray-200 p-2 px-0 text-xs tracking-tight outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 md:text-sm;
|
||||
}
|
||||
|
||||
#svelte .listContainer {
|
||||
@apply bg-coolgray-400 text-white scrollbar-w-2 scrollbar-thumb-green-500 scrollbar-track-coolgray-200;
|
||||
}
|
||||
#svelte .selectedItem {
|
||||
@apply pl-2;
|
||||
}
|
||||
|
||||
#svelte .item.hover {
|
||||
@apply bg-coollabs text-white !important;
|
||||
}
|
||||
#svelte .item.active {
|
||||
@apply bg-coolgray-100 text-white;
|
||||
}
|
||||
|
||||
select {
|
||||
@apply h-12 w-96 rounded bg-coolgray-200 p-2 text-xs font-bold tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:text-stone-600 md:text-sm;
|
||||
}
|
||||
.custom-select-wrapper {
|
||||
--background: rgb(32 32 32);
|
||||
--inputColor: white;
|
||||
--multiItemPadding: 0;
|
||||
--multiSelectPadding: 0 0.5rem 0 0.5rem;
|
||||
--border: none;
|
||||
--placeholderColor: rgb(87 83 78);
|
||||
--listBackground: rgb(32 32 32);
|
||||
--itemColor: white;
|
||||
--itemHoverBG: rgb(107 22 237);
|
||||
--multiItemBG: rgb(32 32 32);
|
||||
--multiClearHoverBG: transparent;
|
||||
--multiClearHoverFill: rgb(239 68 68);
|
||||
--multiItemActiveBG: transparent;
|
||||
--multiClearBG: transparent;
|
||||
--clearSelectFocusColor: white;
|
||||
--clearSelectHoverColor: rgb(239 68 68);
|
||||
--multiItemBorderRadius: 0.25rem;
|
||||
--listShadow: none;
|
||||
}
|
||||
|
||||
label {
|
||||
@apply inline-block;
|
||||
}
|
||||
.btn {
|
||||
@apply text-white text-base min-w-fit no-animation;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply underline hover:text-white;
|
||||
}
|
||||
|
||||
.content {
|
||||
@apply p-2 px-4;
|
||||
}
|
||||
|
||||
.title {
|
||||
@apply text-lg lg:text-2xl font-bold;
|
||||
}
|
||||
.subtitle {
|
||||
@apply text-lg lg:text-xl font-bold text-indigo-300;
|
||||
}
|
||||
.label {
|
||||
@apply text-sm leading-6 font-semibold text-sky-500 dark:text-sky-400;
|
||||
}
|
||||
.card {
|
||||
@apply border bg-coolgray-100 border-coolgray-200 rounded p-2 space-y-2 sticky top-4 mb-2 items-center;
|
||||
}
|
||||
.icon-holder {
|
||||
overflow: hidden;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
margin-right: 8px;
|
||||
background: linear-gradient(0deg, #999, #ddd);
|
||||
}
|
||||
.instance-status-running {
|
||||
box-shadow: 1px 4px 5px #3df721;
|
||||
}
|
||||
.instance-status-stopped {
|
||||
box-shadow: 1px 4px 5px rgb(110, 191, 225);
|
||||
}
|
||||
.instance-status-error {
|
||||
box-shadow: 1px 4px 5px #fb00ff;
|
||||
}
|
||||
.instance-status-degraded {
|
||||
box-shadow: 1px 4px 5px #f7b121;
|
||||
}
|
||||
.badge-status-healthy,
|
||||
.badge-status-running {
|
||||
@apply text-green-500;
|
||||
}
|
||||
.badge-status-degraded {
|
||||
@apply text-green-500;
|
||||
}
|
||||
.badge-status-stopped {
|
||||
@apply text-sky-500;
|
||||
}
|
||||
.delete-button {
|
||||
@apply bg-red-600;
|
||||
}
|
||||
.delete-button:hover {
|
||||
@apply bg-red-500;
|
||||
}
|
||||
/* Interchange menu position */
|
||||
.menu-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.menu-left .menu-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.menu-left .menu-bar > * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.menu-top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.menu-top .menu-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.menu-top .menu-bar > * {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.nav-main {
|
||||
@apply fixed top-0 left-0 min-h-screen w-16 min-w-[4rem] overflow-hidden border-r border-stone-800 bg-coolgray-200 scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200 xl:overflow-visible;
|
||||
}
|
||||
|
||||
.nav-side {
|
||||
@apply absolute right-0 top-0 z-50 m-5 flex flex-wrap items-center justify-end space-x-2 bg-coolblack/40 text-white;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
@apply rounded p-1 transition duration-200;
|
||||
}
|
||||
|
||||
.icons {
|
||||
@apply rounded p-2 transition duration-200 hover:bg-coolgray-500 disabled:bg-coolblack disabled:text-coolgray-500 !important;
|
||||
}
|
||||
|
||||
.arrow-right-applications {
|
||||
@apply -ml-6 px-2 font-bold text-green-500;
|
||||
}
|
||||
|
||||
.border-gradient {
|
||||
border-bottom: 2px solid transparent;
|
||||
-o-border-image: linear-gradient(
|
||||
0.25turn,
|
||||
rgba(255, 249, 34),
|
||||
rgba(255, 0, 128),
|
||||
rgba(56, 2, 155, 0)
|
||||
);
|
||||
border-image: linear-gradient(
|
||||
0.25turn,
|
||||
rgba(255, 249, 34),
|
||||
rgba(255, 0, 128),
|
||||
rgba(56, 2, 155, 0)
|
||||
);
|
||||
border-image-slice: 1;
|
||||
}
|
||||
.border-gradient-full {
|
||||
border: 4px solid transparent;
|
||||
-o-border-image: linear-gradient(
|
||||
0.25turn,
|
||||
rgba(255, 249, 34),
|
||||
rgba(255, 0, 128),
|
||||
rgba(56, 2, 155, 0)
|
||||
);
|
||||
border-image: linear-gradient(
|
||||
0.25turn,
|
||||
rgba(255, 249, 34),
|
||||
rgba(255, 0, 128),
|
||||
rgba(56, 2, 155, 0)
|
||||
);
|
||||
border-image-slice: 1;
|
||||
}
|
||||
|
||||
.box-selection {
|
||||
@apply min-w-[16rem] justify-center rounded border-transparent bg-coolgray-200 p-6 hover:border-transparent hover:bg-coolgray-400;
|
||||
}
|
||||
|
||||
.lds-heart {
|
||||
animation: lds-heart 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
@keyframes lds-heart {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
5% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
39% {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
45% {
|
||||
transform: scale(1);
|
||||
}
|
||||
60% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.sub-menu {
|
||||
@apply w-48 text-base font-bold hover:bg-coolgray-500 rounded p-2 hover:text-white text-stone-200 cursor-pointer;
|
||||
}
|
||||
|
||||
.sub-menu-active {
|
||||
@apply bg-coolgray-500 text-white;
|
||||
}
|
||||
|
||||
.table tbody td,
|
||||
.table tbody th,
|
||||
.table thead th {
|
||||
background-color: transparent;
|
||||
}
|
||||
.table * {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
@apply flex flex-row z-10 w-full py-5 px-5;
|
||||
}
|
||||
.burger {
|
||||
@apply block m-[2px] h-[3px] w-5 rounded;
|
||||
}
|
||||
|
||||
.bg-coollabs-gradient {
|
||||
@apply bg-gradient-to-r from-purple-500 via-pink-500 to-red-500;
|
||||
}
|
201
apps/client/src/lib/common.ts
Normal file
201
apps/client/src/lib/common.ts
Normal file
@ -0,0 +1,201 @@
|
||||
import { dev } from '$app/environment';
|
||||
import { addToast } from './store';
|
||||
import Cookies from 'js-cookie';
|
||||
export const asyncSleep = (delay: number) => new Promise((resolve) => setTimeout(resolve, delay));
|
||||
|
||||
export function errorNotification(error: any | { message: string }): void {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message)
|
||||
addToast({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
} else {
|
||||
console.error(error)
|
||||
addToast({
|
||||
message: error,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
export function getRndInteger(min: number, max: number) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
export function getDomain(domain: string) {
|
||||
return domain?.replace('https://', '').replace('http://', '');
|
||||
}
|
||||
|
||||
export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno', 'laravel', 'heroku'];
|
||||
export const staticDeployments = [
|
||||
'react',
|
||||
'vuejs',
|
||||
'static',
|
||||
'svelte',
|
||||
'gatsby',
|
||||
'php',
|
||||
'astro',
|
||||
'eleventy'
|
||||
];
|
||||
|
||||
export function getAPIUrl() {
|
||||
if (GITPOD_WORKSPACE_URL) {
|
||||
const { href } = new URL(GITPOD_WORKSPACE_URL);
|
||||
const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, '');
|
||||
return newURL;
|
||||
}
|
||||
if (CODESANDBOX_HOST) {
|
||||
return `https://${CODESANDBOX_HOST.replace(/\$PORT/, '3001')}`;
|
||||
}
|
||||
return dev ? `http://${window.location.hostname}:3001` : 'http://localhost:3000';
|
||||
}
|
||||
export function getWebhookUrl(type: string) {
|
||||
if (GITPOD_WORKSPACE_URL) {
|
||||
const { href } = new URL(GITPOD_WORKSPACE_URL);
|
||||
const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, '');
|
||||
if (type === 'github') {
|
||||
return `${newURL}/webhooks/github/events`;
|
||||
}
|
||||
if (type === 'gitlab') {
|
||||
return `${newURL}/webhooks/gitlab/events`;
|
||||
}
|
||||
}
|
||||
if (CODESANDBOX_HOST) {
|
||||
const newURL = `https://${CODESANDBOX_HOST.replace(/\$PORT/, '3001')}`;
|
||||
if (type === 'github') {
|
||||
return `${newURL}/webhooks/github/events`;
|
||||
}
|
||||
if (type === 'gitlab') {
|
||||
return `${newURL}/webhooks/gitlab/events`;
|
||||
}
|
||||
}
|
||||
return `https://webhook.site/0e5beb2c-4e9b-40e2-a89e-32295e570c21/events`;
|
||||
}
|
||||
|
||||
async function send({
|
||||
method,
|
||||
path,
|
||||
data = null,
|
||||
headers,
|
||||
timeout = 120000
|
||||
}: {
|
||||
method: string;
|
||||
path: string;
|
||||
data?: any;
|
||||
headers?: any;
|
||||
timeout?: number;
|
||||
}): Promise<Record<string, unknown>> {
|
||||
const token = Cookies.get('token');
|
||||
const controller = new AbortController();
|
||||
const id = setTimeout(() => controller.abort(), timeout);
|
||||
const opts: any = { method, headers: {}, body: null, signal: controller.signal };
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
const parsedData = data;
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (value === '') {
|
||||
parsedData[key] = null;
|
||||
}
|
||||
}
|
||||
if (parsedData) {
|
||||
opts.headers['Content-Type'] = 'application/json';
|
||||
opts.body = JSON.stringify(parsedData);
|
||||
}
|
||||
}
|
||||
|
||||
if (headers) {
|
||||
opts.headers = {
|
||||
...opts.headers,
|
||||
...headers
|
||||
};
|
||||
}
|
||||
if (token && !path.startsWith('https://')) {
|
||||
opts.headers = {
|
||||
...opts.headers,
|
||||
Authorization: `Bearer ${token}`
|
||||
};
|
||||
}
|
||||
if (!path.startsWith('https://')) {
|
||||
path = `/api/v1${path}`;
|
||||
}
|
||||
|
||||
if (dev && !path.startsWith('https://')) {
|
||||
path = `${getAPIUrl()}${path}`;
|
||||
}
|
||||
if (method === 'POST' && data && !opts.body) {
|
||||
opts.body = data;
|
||||
}
|
||||
const response = await fetch(`${path}`, opts);
|
||||
|
||||
clearTimeout(id);
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
let responseData = {};
|
||||
if (contentType) {
|
||||
if (contentType?.indexOf('application/json') !== -1) {
|
||||
responseData = await response.json();
|
||||
} else if (contentType?.indexOf('text/plain') !== -1) {
|
||||
responseData = await response.text();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
if (!response.ok) {
|
||||
if (
|
||||
response.status === 401 &&
|
||||
!path.startsWith('https://api.github') &&
|
||||
!path.includes('/v4/')
|
||||
) {
|
||||
Cookies.remove('token');
|
||||
}
|
||||
|
||||
throw responseData;
|
||||
}
|
||||
return responseData;
|
||||
}
|
||||
|
||||
export function get(path: string, headers?: Record<string, unknown>): Promise<Record<string, any>> {
|
||||
return send({ method: 'GET', path, headers });
|
||||
}
|
||||
|
||||
export function del(
|
||||
path: string,
|
||||
data: Record<string, unknown>,
|
||||
headers?: Record<string, unknown>
|
||||
): Promise<Record<string, any>> {
|
||||
return send({ method: 'DELETE', path, data, headers });
|
||||
}
|
||||
|
||||
export function post(
|
||||
path: string,
|
||||
data: Record<string, unknown> | FormData,
|
||||
headers?: Record<string, unknown>
|
||||
): Promise<Record<string, any>> {
|
||||
return send({ method: 'POST', path, data, headers });
|
||||
}
|
||||
|
||||
export function put(
|
||||
path: string,
|
||||
data: Record<string, unknown>,
|
||||
headers?: Record<string, unknown>
|
||||
): Promise<Record<string, any>> {
|
||||
return send({ method: 'PUT', path, data, headers });
|
||||
}
|
||||
export function changeQueryParams(buildId: string) {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
queryParams.set('buildId', buildId);
|
||||
// @ts-ignore
|
||||
return history.pushState(null, null, '?' + queryParams.toString());
|
||||
}
|
||||
|
||||
export const dateOptions: any = {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
hour12: false
|
||||
};
|
1
apps/client/src/lib/components/Beta.svelte
Normal file
1
apps/client/src/lib/components/Beta.svelte
Normal file
@ -0,0 +1 @@
|
||||
<span class="badge bg-coollabs-gradient rounded text-white font-normal"> BETA </span>
|
156
apps/client/src/lib/components/CopyPasswordField.svelte
Normal file
156
apps/client/src/lib/components/CopyPasswordField.svelte
Normal file
@ -0,0 +1,156 @@
|
||||
<script lang="ts">
|
||||
import { browser } from '$app/environment';
|
||||
import { addToast } from '$lib/store';
|
||||
let showPassword = false;
|
||||
|
||||
export let value: string;
|
||||
export let disabled = false;
|
||||
export let isPasswordField = false;
|
||||
export let readonly = false;
|
||||
export let textarea = false;
|
||||
export let required = false;
|
||||
export let pattern: string | null | undefined = null;
|
||||
export let id: string;
|
||||
export let name: string;
|
||||
export let placeholder = '';
|
||||
export let inputStyle = '';
|
||||
|
||||
let disabledClass = 'input input-primary bg-coolback disabled:bg-coolblack w-full';
|
||||
let isHttps = browser && window.location.protocol === 'https:';
|
||||
|
||||
function copyToClipboard() {
|
||||
if (isHttps && navigator.clipboard) {
|
||||
navigator.clipboard.writeText(value);
|
||||
addToast({
|
||||
message: 'Copied to clipboard.',
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="relative">
|
||||
{#if !isPasswordField || showPassword}
|
||||
{#if textarea}
|
||||
<textarea
|
||||
style={inputStyle}
|
||||
rows="5"
|
||||
class={disabledClass}
|
||||
class:pr-10={true}
|
||||
class:pr-20={value && isHttps}
|
||||
class:border={required && !value}
|
||||
class:border-red-500={required && !value}
|
||||
{placeholder}
|
||||
type="text"
|
||||
{id}
|
||||
{pattern}
|
||||
{required}
|
||||
{readonly}
|
||||
{disabled}
|
||||
{name}>{value}</textarea
|
||||
>
|
||||
{:else}
|
||||
<input
|
||||
style={inputStyle}
|
||||
class={disabledClass}
|
||||
type="text"
|
||||
class:pr-10={true}
|
||||
class:pr-20={value && isHttps}
|
||||
class:border={required && !value}
|
||||
class:border-red-500={required && !value}
|
||||
{id}
|
||||
{name}
|
||||
{required}
|
||||
{pattern}
|
||||
{readonly}
|
||||
bind:value
|
||||
{disabled}
|
||||
{placeholder}
|
||||
/>
|
||||
{/if}
|
||||
{:else}
|
||||
<input
|
||||
style={inputStyle}
|
||||
class={disabledClass}
|
||||
class:pr-10={true}
|
||||
class:pr-20={value && isHttps}
|
||||
class:border={required && !value}
|
||||
class:border-red-500={required && !value}
|
||||
type="password"
|
||||
{id}
|
||||
{name}
|
||||
{readonly}
|
||||
{pattern}
|
||||
{required}
|
||||
bind:value
|
||||
{disabled}
|
||||
{placeholder}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div class="absolute top-0 right-0 flex justify-center items-center h-full cursor-pointer text-stone-600 hover:text-white mr-3">
|
||||
<div class="flex space-x-2">
|
||||
{#if isPasswordField}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div on:click={() => (showPassword = !showPassword)}>
|
||||
{#if showPassword}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if value && isHttps}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div on:click={copyToClipboard}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<rect x="8" y="8" width="12" height="12" rx="2" />
|
||||
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
|
||||
</svg>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
38
apps/client/src/lib/components/Explainer.svelte
Normal file
38
apps/client/src/lib/components/Explainer.svelte
Normal file
@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
// import { onMount } from 'svelte';
|
||||
|
||||
// import Tooltip from './Tooltip.svelte';
|
||||
export let explanation = '';
|
||||
export let position = 'dropdown-right';
|
||||
// let id: any;
|
||||
// let self: any;
|
||||
// onMount(() => {
|
||||
// id = `info-${self.offsetLeft}-${self.offsetTop}`;
|
||||
// });
|
||||
</script>
|
||||
|
||||
<div class={`dropdown dropdown-end ${position}`}>
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-sky-500">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
class="w-4 h-4 stroke-current"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/></svg
|
||||
>
|
||||
</label>
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<div tabindex="0" class="card compact dropdown-content shadow bg-coolgray-400 rounded w-64">
|
||||
<div class="card-body">
|
||||
<!-- <h2 class="card-title">You needed more info?</h2> -->
|
||||
<p class="text-xs font-normal">{@html explanation}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
87
apps/client/src/lib/components/Setting.svelte
Normal file
87
apps/client/src/lib/components/Setting.svelte
Normal file
@ -0,0 +1,87 @@
|
||||
<script lang="ts">
|
||||
import Beta from './Beta.svelte';
|
||||
import Explaner from './Explainer.svelte';
|
||||
import Tooltip from './Tooltip.svelte';
|
||||
|
||||
export let id: any;
|
||||
export let customClass: any = null;
|
||||
export let setting: any;
|
||||
export let title: any;
|
||||
export let isBeta: any = false;
|
||||
export let description: any = null;
|
||||
export let isCenter = true;
|
||||
export let disabled = false;
|
||||
export let dataTooltip: any = null;
|
||||
export let loading = false;
|
||||
let triggeredBy = `#${id}`;
|
||||
</script>
|
||||
|
||||
<div class="flex items-center py-4 pr-8">
|
||||
<div class="flex w-96 flex-col">
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<label>
|
||||
{title}
|
||||
{#if isBeta}
|
||||
<Beta />
|
||||
{/if}
|
||||
{#if description && description !== ''}
|
||||
<Explaner explanation={description} />
|
||||
{/if}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class:text-center={isCenter} class={`flex justify-center ${customClass}`}>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:click
|
||||
aria-pressed="false"
|
||||
class="relative mx-20 inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out"
|
||||
class:opacity-50={disabled || loading}
|
||||
class:bg-green-600={!loading && setting}
|
||||
class:bg-stone-700={!loading && !setting}
|
||||
class:bg-yellow-500={loading}
|
||||
{id}
|
||||
>
|
||||
<span class="sr-only">Use setting</span>
|
||||
<span
|
||||
class="pointer-events-none relative inline-block h-5 w-5 transform rounded-full bg-white shadow transition duration-200 ease-in-out"
|
||||
class:translate-x-5={setting}
|
||||
class:translate-x-0={!setting}
|
||||
>
|
||||
<span
|
||||
class=" absolute inset-0 flex h-full w-full items-center justify-center transition-opacity duration-200 ease-in"
|
||||
class:opacity-0={setting}
|
||||
class:opacity-100={!setting}
|
||||
class:animate-spin={loading}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg class="h-3 w-3 bg-white text-red-600" fill="none" viewBox="0 0 12 12">
|
||||
<path
|
||||
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="absolute inset-0 flex h-full w-full items-center justify-center transition-opacity duration-100 ease-out"
|
||||
aria-hidden="true"
|
||||
class:opacity-100={setting}
|
||||
class:opacity-0={!setting}
|
||||
class:animate-spin={loading}
|
||||
>
|
||||
<svg class="h-3 w-3 bg-white text-green-600" fill="currentColor" viewBox="0 0 12 12">
|
||||
<path
|
||||
d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if dataTooltip}
|
||||
<Tooltip {triggeredBy} placement="top">{dataTooltip}</Tooltip>
|
||||
{/if}
|
64
apps/client/src/lib/components/Toast.svelte
Normal file
64
apps/client/src/lib/components/Toast.svelte
Normal file
@ -0,0 +1,64 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
export let type = 'info';
|
||||
function success() {
|
||||
if (type === 'success') {
|
||||
return 'bg-dark lg:bg-primary';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:click={() => dispatch('click')}
|
||||
on:mouseover={() => dispatch('pause')}
|
||||
on:focus={() => dispatch('pause')}
|
||||
on:mouseout={() => dispatch('resume')}
|
||||
on:blur={() => dispatch('resume')}
|
||||
class={` flex flex-row justify-center alert shadow-lg text-white hover:scale-105 transition-all duration-100 cursor-pointer rounded ${success()}`}
|
||||
class:alert-error={type === 'error'}
|
||||
class:alert-info={type === 'info'}
|
||||
>
|
||||
{#if type === 'success'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="stroke-current flex-shrink-0 h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/></svg
|
||||
>
|
||||
{:else if type === 'error'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="stroke-current flex-shrink-0 h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/></svg
|
||||
>
|
||||
{:else if type === 'info'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
class="stroke-current flex-shrink-0 w-6 h-6"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/></svg
|
||||
>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
25
apps/client/src/lib/components/Toasts.svelte
Normal file
25
apps/client/src/lib/components/Toasts.svelte
Normal file
@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import Toast from './Toast.svelte';
|
||||
import { dismissToast, pauseToast, resumeToast, toasts } from '$lib/store';
|
||||
</script>
|
||||
|
||||
{#if $toasts.length > 0}
|
||||
<section>
|
||||
<article class="toast toast-top toast-center rounded-none w-2/3 lg:w-[20rem]" role="alert">
|
||||
{#each $toasts as toast (toast.id)}
|
||||
<Toast
|
||||
type={toast.type}
|
||||
on:resume={() => resumeToast(toast.id)}
|
||||
on:pause={() => pauseToast(toast.id)}
|
||||
on:click={() => dismissToast(toast.id)}>{@html toast.message}</Toast
|
||||
>
|
||||
{/each}
|
||||
</article>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
section {
|
||||
@apply fixed top-0 left-0 right-0 w-full flex flex-col mt-4 justify-center z-[1000];
|
||||
}
|
||||
</style>
|
10
apps/client/src/lib/components/Tooltip.svelte
Normal file
10
apps/client/src/lib/components/Tooltip.svelte
Normal file
@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Tooltip } from 'flowbite-svelte';
|
||||
export let placement = 'bottom';
|
||||
export let color = 'bg-coollabs';
|
||||
export let triggeredBy = '#tooltip-default';
|
||||
</script>
|
||||
|
||||
<Tooltip {triggeredBy} {placement} arrow={false} defaultClass={color + ' font-thin text-xs text-left border-none p-2'} style="custom"
|
||||
><slot /></Tooltip
|
||||
>
|
206
apps/client/src/lib/components/UpdateAvailable.svelte
Normal file
206
apps/client/src/lib/components/UpdateAvailable.svelte
Normal file
@ -0,0 +1,206 @@
|
||||
<script lang="ts">
|
||||
import { dev } from '$app/environment';
|
||||
import {
|
||||
addToast,
|
||||
appSession,
|
||||
features,
|
||||
updateLoading,
|
||||
isUpdateAvailable,
|
||||
latestVersion
|
||||
} from '$lib/store';
|
||||
import { asyncSleep, errorNotification } from '$lib/common';
|
||||
import { onMount } from 'svelte';
|
||||
import Tooltip from './Tooltip.svelte';
|
||||
|
||||
let updateStatus: any = {
|
||||
found: false,
|
||||
loading: false,
|
||||
success: null
|
||||
};
|
||||
async function update() {
|
||||
updateStatus.loading = true;
|
||||
try {
|
||||
if (dev) {
|
||||
localStorage.setItem('lastVersion', $appSession.version);
|
||||
await asyncSleep(1000);
|
||||
updateStatus.loading = false;
|
||||
return window.location.reload();
|
||||
} else {
|
||||
localStorage.setItem('lastVersion', $appSession.version);
|
||||
// await post(`/update`, { type: 'update', latestVersion: $latestVersion });
|
||||
addToast({
|
||||
message: 'Update completed.<br><br>Waiting for the new version to start...',
|
||||
type: 'success'
|
||||
});
|
||||
|
||||
let reachable = false;
|
||||
let tries = 0;
|
||||
do {
|
||||
await asyncSleep(4000);
|
||||
try {
|
||||
// await get(`/undead`);
|
||||
reachable = true;
|
||||
} catch (error) {
|
||||
reachable = false;
|
||||
}
|
||||
if (reachable) break;
|
||||
tries++;
|
||||
} while (!reachable || tries < 120);
|
||||
addToast({
|
||||
message: 'New version reachable. Reloading...',
|
||||
type: 'success'
|
||||
});
|
||||
updateStatus.loading = false;
|
||||
updateStatus.success = true;
|
||||
await asyncSleep(3000);
|
||||
return window.location.reload();
|
||||
}
|
||||
} catch (error) {
|
||||
updateStatus.success = false;
|
||||
updateStatus.loading = false;
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
onMount(async () => {
|
||||
if ($appSession.userId) {
|
||||
const overrideVersion = $features.latestVersion;
|
||||
if ($appSession.teamId === '0') {
|
||||
if ($updateLoading === true) return;
|
||||
try {
|
||||
$updateLoading = true;
|
||||
// const data = await get(`/update`);
|
||||
if (overrideVersion || data?.isUpdateAvailable) {
|
||||
$latestVersion = overrideVersion || data.latestVersion;
|
||||
if (overrideVersion) {
|
||||
$isUpdateAvailable = true;
|
||||
} else {
|
||||
$isUpdateAvailable = data.isUpdateAvailable;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
$updateLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="py-0 lg:py-2">
|
||||
{#if $appSession.teamId === '0'}
|
||||
{#if $isUpdateAvailable}
|
||||
<button
|
||||
id="update"
|
||||
disabled={updateStatus.success === false}
|
||||
on:click={update}
|
||||
class="icons bg-coollabs-gradient text-white duration-75 hover:scale-105 w-full"
|
||||
>
|
||||
{#if updateStatus.loading}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="lds-heart h-8 w-8 mx-auto"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M19.5 13.572l-7.5 7.428l-7.5 -7.428m0 0a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572"
|
||||
/>
|
||||
</svg>
|
||||
{:else if updateStatus.success === null}
|
||||
<div class="flex items-center justify-center space-x-2">
|
||||
<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" />
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<line x1="12" y1="8" x2="8" y2="12" />
|
||||
<line x1="12" y1="8" x2="12" y2="16" />
|
||||
<line x1="16" y1="12" x2="12" y2="8" />
|
||||
</svg>
|
||||
<span class="flex lg:hidden">Update available</span>
|
||||
</div>
|
||||
{:else if updateStatus.success}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" class="h-8 w-8"
|
||||
><path
|
||||
fill="#DD2E44"
|
||||
d="M11.626 7.488c-.112.112-.197.247-.268.395l-.008-.008L.134 33.141l.011.011c-.208.403.14 1.223.853 1.937.713.713 1.533 1.061 1.936.853l.01.01L28.21 24.735l-.008-.009c.147-.07.282-.155.395-.269 1.562-1.562-.971-6.627-5.656-11.313-4.687-4.686-9.752-7.218-11.315-5.656z"
|
||||
/><path
|
||||
fill="#EA596E"
|
||||
d="M13 12L.416 32.506l-.282.635.011.011c-.208.403.14 1.223.853 1.937.232.232.473.408.709.557L17 17l-4-5z"
|
||||
/><path
|
||||
fill="#A0041E"
|
||||
d="M23.012 13.066c4.67 4.672 7.263 9.652 5.789 11.124-1.473 1.474-6.453-1.118-11.126-5.788-4.671-4.672-7.263-9.654-5.79-11.127 1.474-1.473 6.454 1.119 11.127 5.791z"
|
||||
/><path
|
||||
fill="#AA8DD8"
|
||||
d="M18.59 13.609c-.199.161-.459.245-.734.215-.868-.094-1.598-.396-2.109-.873-.541-.505-.808-1.183-.735-1.862.128-1.192 1.324-2.286 3.363-2.066.793.085 1.147-.17 1.159-.292.014-.121-.277-.446-1.07-.532-.868-.094-1.598-.396-2.11-.873-.541-.505-.809-1.183-.735-1.862.13-1.192 1.325-2.286 3.362-2.065.578.062.883-.057 1.012-.134.103-.063.144-.123.148-.158.012-.121-.275-.446-1.07-.532-.549-.06-.947-.552-.886-1.102.059-.549.55-.946 1.101-.886 2.037.219 2.973 1.542 2.844 2.735-.13 1.194-1.325 2.286-3.364 2.067-.578-.063-.88.057-1.01.134-.103.062-.145.123-.149.157-.013.122.276.446 1.071.532 2.037.22 2.973 1.542 2.844 2.735-.129 1.192-1.324 2.286-3.362 2.065-.578-.062-.882.058-1.012.134-.104.064-.144.124-.148.158-.013.121.276.446 1.07.532.548.06.947.553.886 1.102-.028.274-.167.511-.366.671z"
|
||||
/><path
|
||||
fill="#77B255"
|
||||
d="M30.661 22.857c1.973-.557 3.334.323 3.658 1.478.324 1.154-.378 2.615-2.35 3.17-.77.216-1.001.584-.97.701.034.118.425.312 1.193.095 1.972-.555 3.333.325 3.657 1.479.326 1.155-.378 2.614-2.351 3.17-.769.216-1.001.585-.967.702.033.117.423.311 1.192.095.53-.149 1.084.16 1.233.691.148.532-.161 1.084-.693 1.234-1.971.555-3.333-.323-3.659-1.479-.324-1.154.379-2.613 2.353-3.169.77-.217 1.001-.584.967-.702-.032-.117-.422-.312-1.19-.096-1.974.556-3.334-.322-3.659-1.479-.325-1.154.378-2.613 2.351-3.17.768-.215.999-.585.967-.701-.034-.118-.423-.312-1.192-.096-.532.15-1.083-.16-1.233-.691-.149-.53.161-1.082.693-1.232z"
|
||||
/><path
|
||||
fill="#AA8DD8"
|
||||
d="M23.001 20.16c-.294 0-.584-.129-.782-.375-.345-.432-.274-1.061.156-1.406.218-.175 5.418-4.259 12.767-3.208.547.078.927.584.849 1.131-.078.546-.58.93-1.132.848-6.493-.922-11.187 2.754-11.233 2.791-.186.148-.406.219-.625.219z"
|
||||
/><path
|
||||
fill="#77B255"
|
||||
d="M5.754 16c-.095 0-.192-.014-.288-.042-.529-.159-.829-.716-.67-1.245 1.133-3.773 2.16-9.794.898-11.364-.141-.178-.354-.353-.842-.316-.938.072-.849 2.051-.848 2.071.042.551-.372 1.031-.922 1.072-.559.034-1.031-.372-1.072-.923-.103-1.379.326-4.035 2.692-4.214 1.056-.08 1.933.287 2.552 1.057 2.371 2.951-.036 11.506-.542 13.192-.13.433-.528.712-.958.712z"
|
||||
/><circle fill="#5C913B" cx="25.5" cy="9.5" r="1.5" /><circle
|
||||
fill="#9266CC"
|
||||
cx="2"
|
||||
cy="18"
|
||||
r="2"
|
||||
/><circle fill="#5C913B" cx="32.5" cy="19.5" r="1.5" /><circle
|
||||
fill="#5C913B"
|
||||
cx="23.5"
|
||||
cy="31.5"
|
||||
r="1.5"
|
||||
/><circle fill="#FFCC4D" cx="28" cy="4" r="2" /><circle
|
||||
fill="#FFCC4D"
|
||||
cx="32.5"
|
||||
cy="8.5"
|
||||
r="1.5"
|
||||
/><circle fill="#FFCC4D" cx="29.5" cy="12.5" r="1.5" /><circle
|
||||
fill="#FFCC4D"
|
||||
cx="7.5"
|
||||
cy="23.5"
|
||||
r="1.5"
|
||||
/></svg
|
||||
>
|
||||
{:else}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" class="h-9 w-8"
|
||||
><path
|
||||
fill="#FFCC4D"
|
||||
d="M36 18c0 9.941-8.059 18-18 18S0 27.941 0 18 8.059 0 18 0s18 8.059 18 18"
|
||||
/><path
|
||||
fill="#664500"
|
||||
d="M22 27c0 2.763-1.791 3-4 3-2.21 0-4-.237-4-3 0-2.761 1.79-6 4-6 2.209 0 4 3.239 4 6zm8-12c-.124 0-.25-.023-.371-.072-5.229-2.091-7.372-5.241-7.461-5.374-.307-.46-.183-1.081.277-1.387.459-.306 1.077-.184 1.385.274.019.027 1.93 2.785 6.541 4.629.513.206.763.787.558 1.3-.157.392-.533.63-.929.63zM6 15c-.397 0-.772-.238-.929-.629-.205-.513.044-1.095.557-1.3 4.612-1.844 6.523-4.602 6.542-4.629.308-.456.929-.577 1.387-.27.457.308.581.925.275 1.383-.089.133-2.232 3.283-7.46 5.374C6.25 14.977 6.124 15 6 15z"
|
||||
/><path fill="#5DADEC" d="M24 16h4v19l-4-.046V16zM8 35l4-.046V16H8v19z" /><path
|
||||
fill="#664500"
|
||||
d="M14.999 18c-.15 0-.303-.034-.446-.105-3.512-1.756-7.07-.018-7.105 0-.495.249-1.095.046-1.342-.447-.247-.494-.047-1.095.447-1.342.182-.09 4.498-2.197 8.895 0 .494.247.694.848.447 1.342-.176.35-.529.552-.896.552zm14 0c-.15 0-.303-.034-.446-.105-3.513-1.756-7.07-.018-7.105 0-.494.248-1.094.047-1.342-.447-.247-.494-.047-1.095.447-1.342.182-.09 4.501-2.196 8.895 0 .494.247.694.848.447 1.342-.176.35-.529.552-.896.552z"
|
||||
/><ellipse fill="#5DADEC" cx="18" cy="34" rx="18" ry="2" /><ellipse
|
||||
fill="#E75A70"
|
||||
cx="18"
|
||||
cy="27"
|
||||
rx="3"
|
||||
ry="2"
|
||||
/></svg
|
||||
>
|
||||
{/if}
|
||||
</button>
|
||||
<Tooltip triggeredBy="#update" placement="right" color="bg-coolgray-200 text-white"
|
||||
>New Version Available!</Tooltip
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
17
apps/client/src/lib/components/icons/Delete.svelte
Normal file
17
apps/client/src/lib/components/icons/Delete.svelte
Normal file
@ -0,0 +1,17 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<line x1="4" y1="7" x2="20" y2="7" />
|
||||
<line x1="10" y1="11" x2="10" y2="17" />
|
||||
<line x1="14" y1="11" x2="14" y2="17" />
|
||||
<path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" />
|
||||
<path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" />
|
||||
</svg>
|
After Width: | Height: | Size: 486 B |
10
apps/client/src/lib/components/icons/RemoteLink.svelte
Normal file
10
apps/client/src/lib/components/icons/RemoteLink.svelte
Normal file
@ -0,0 +1,10 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="3"
|
||||
stroke="currentColor"
|
||||
class="w-3 h-3 text-white"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 19.5l15-15m0 0H8.25m11.25 0v11.25" />
|
||||
</svg>
|
After Width: | Height: | Size: 262 B |
@ -0,0 +1,47 @@
|
||||
<script lang="ts">
|
||||
import * as Icons from '$lib/components/icons/applications';
|
||||
export let application: any;
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
{#if application.buildPack?.toLowerCase() === 'rust'}
|
||||
<Icons.Rust {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'node'}
|
||||
<Icons.Nodejs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'react'}
|
||||
<Icons.React {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'svelte'}
|
||||
<Icons.Svelte {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'vuejs'}
|
||||
<Icons.Vuejs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'php'}
|
||||
<Icons.Php {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'python'}
|
||||
<Icons.Python {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'static'}
|
||||
<Icons.Static {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'nestjs'}
|
||||
<Icons.Nestjs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'nuxtjs'}
|
||||
<Icons.Nuxtjs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'nextjs'}
|
||||
<Icons.Nextjs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'gatsby'}
|
||||
<Icons.Gatsby {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'docker'}
|
||||
<Icons.Docker {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'astro'}
|
||||
<Icons.Astro {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'eleventy'}
|
||||
<Icons.Eleventy {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'deno'}
|
||||
<Icons.Deno {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'laravel'}
|
||||
<Icons.Laravel {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'heroku'}
|
||||
<Icons.Heroku {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'compose'}
|
||||
<Icons.Compose {isAbsolute} />
|
||||
{:else if application.simpleDockerfile}
|
||||
<Icons.Docker {isAbsolute} />
|
||||
{/if}
|
@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-6 h-14 w-14' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 256 256"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
id="a"
|
||||
fill="#302649"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
|
||||
/>
|
||||
<path
|
||||
id="flame"
|
||||
fill="#EF661E"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<img
|
||||
alt="docker compose logo"
|
||||
class={isAbsolute ? 'w-16 h-16 absolute top-0 left-0 -m-8' : 'w-8 h-8 mx-auto'}
|
||||
src="/icons/compose.png"
|
||||
/>
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
|
||||
<svg viewBox="0 0 128 128" class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-12 w-12' : 'mx-auto w-10 h-10'}>
|
||||
<path d="M124.8 52.1c-4.3-2.5-10-2.8-14.8-1.4-.6-5.2-4-9.7-8-12.9l-1.6-1.3-1.4 1.6c-2.7 3.1-3.5 8.3-3.1 12.3.3 2.9 1.2 5.9 3 8.3-1.4.8-2.9 1.9-4.3 2.4-2.8 1-5.9 2-8.9 2H79V49H66V24H51v12H26v13H13v14H1.8l-.2 1.5c-.5 6.4.3 12.6 3 18.5l1.1 2.2.1.2c7.9 13.4 21.7 19 36.8 19 29.2 0 53.3-13.1 64.3-40.6 7.4.4 15-1.8 18.6-8.9l.9-1.8-1.6-1zM28 39h10v11H28V39zm13.1 44.2c0 1.7-1.4 3.1-3.1 3.1-1.7 0-3.1-1.4-3.1-3.1 0-1.7 1.4-3.1 3.1-3.1 1.7.1 3.1 1.4 3.1 3.1zM28 52h10v11H28V52zm-13 0h11v11H15V52zm27.7 50.2c-15.8-.1-24.3-5.4-31.3-12.4 2.1.1 4.1.2 5.9.2 1.6 0 3.2 0 4.7-.1 3.9-.2 7.3-.7 10.1-1.5 2.3 5.3 6.5 10.2 14 13.8h-3.4zM51 63H40V52h11v11zm0-13H40V39h11v11zm13 13H53V52h11v11zm0-13H53V39h11v11zm0-13H53V26h11v11zm13 26H66V52h11v11zM38.8 81.2c-.2-.1-.5-.2-.8-.2-1.2 0-2.2 1-2.2 2.2 0 1.2 1 2.2 2.2 2.2s2.2-1 2.2-2.2c0-.3-.1-.6-.2-.8-.2.3-.4.5-.8.5-.5 0-.9-.4-.9-.9.1-.4.3-.7.5-.8z" fill="#019BC6"></path>
|
||||
</svg>
|
||||
|
@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-8 h-16 w-16' : 'mx-auto w-8 h-8'}
|
||||
>
|
||||
<path fill="transparent" d="M18 0h92v128H18z" /><path
|
||||
d="M55.3 36.3h.4c1.1 0 1.5.9 1.5 2.3v41.8c0 1.8-.4 3-1.6 3l-4.8-.1c-1.2 0-1.6-1-1.6-3V45.5l-2.1.5c-1 0-1.5-.8-1.5-2.2v-3c0-1.2.4-2 1.4-2.2l8.3-2.2zm16 36.1l.1 3 .6 1.3.6.6.8.1h2.2c1 0 1.7.8 1.7 2v1.9c0 1.2-.6 2-1.8 2h-3.2l-2.3-.1c-.7-.2-1.4-.5-2.2-1a5.7 5.7 0 01-2-1.9c-.4-.8-.8-1.9-1-3.2-.4-1.4-.5-3-.5-4.8v-16h-1.5c-1.1 0-1.6-1-1.6-2.4v-1.7c0-1.4.5-2.3 1.6-2.3h1.5v-.1l.6-12.3c0-1.5.5-2.5 1.6-2.5h3.1c1.2 0 1.6 1 1.6 2.5v12.3h3.6c1.1 0 1.6 1 1.6 2.4V54c0 1.4-.5 2.3-1.6 2.3h-3.6v16.2zm9.4 15.7c0-2 .3-3.2 1.5-3.2.2 0 .4 0 1.4.4l1.1.3.2-.1.4-.7c.3-.6.4-1.6.4-3l-.6-3.3L79 52.7v-.9c0-1.2.3-2 1.2-2H84c.5 0 1 .3 1.3.6.3.4.5.9.6 1.6l3.4 18 2.6-17.8c.1-.8.3-1.3.6-1.7.3-.4.8-.6 1.3-.6h2.6c1 0 1.4.8 1.4 2l-.1.8L92 82.2c-.5 2.5-1 4.4-1.5 5.7a6.6 6.6 0 01-2 3c-.9.6-1.9.9-3.1.9h-.3c-2 0-3.3-.6-4.1-1.7-.3-.4-.4-1-.4-2zM31.3 38.8l8.2-2.1h.5c1 0 1.4.8 1.4 2.2v41.9c0 1.8-.4 2.9-1.6 2.9h-4.7c-1.2 0-1.6-1.1-1.6-3v-35l-2 .6c-1.2 0-1.6-.9-1.6-2.2v-3c0-1.2.4-2 1.4-2.3z"
|
||||
fill="#FFF"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<path
|
||||
fill="#64328B"
|
||||
d="M64,0C28.7,0,0,28.7,0,64v0c0,35.3,28.7,64,64,64s64-28.7,64-64v0C128,28.7,99.3,0,64,0z M13.2,64L64,114.8 C35.9,114.8,13.2,92.1,13.2,64z M75.4,113.5l-60.9-61C19.7,30,39.9,13.2,64,13.2c16.6,0,31.3,7.9,40.5,20.2l-7.5,7.2 C89.7,30.2,77.7,23.5,64,23.5c-17.6,0-32.5,11.2-38.1,26.8C33.1,57,75.4,98.8,78.1,102c12.7-4.7,22.3-15.5,25.4-28.9H81.9v-9.4 l33,0.2C114.8,88.2,98,108.4,75.4,113.5z"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8 '}
|
||||
viewBox="0 0 72 80"
|
||||
>
|
||||
<path
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="#430098"
|
||||
d="M64.8,0 L7.2,0 C3.224,0 0,3.224 0,7.2 L0,72.8 C0,76.776 3.224,80 7.2,80 L64.8,80 C68.776,80 72,76.776 72,72.8 L72,7.2 C72,3.224 68.776,0 64.8,0 Z M18,68 L18,52 L27,60 L18,68 Z M46,68 L46,44.11 C45.961,42.243 45.062,40 41,40 C32.866,40 23.742,44.091 23.651,44.132 L18,46.692 L18,12 L26,12 L26,34.711 C29.994,33.411 35.577,32 41,32 C45.945,32 48.905,33.944 50.517,35.575 C53.958,39.055 54.005,43.488 54.0002258,44 L54.0002258,68 L46,68 Z M48,25 L40,25 C43.144,20.875 45.118,16.534 46,12 L54,12 C53.46,16.544 51.618,20.9 48,25 Z"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 50 52"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><title>Logomark</title><path
|
||||
d="M49.626 11.564a.809.809 0 0 1 .028.209v10.972a.8.8 0 0 1-.402.694l-9.209 5.302V39.25c0 .286-.152.55-.4.694L20.42 51.01c-.044.025-.092.041-.14.058-.018.006-.035.017-.054.022a.805.805 0 0 1-.41 0c-.022-.006-.042-.018-.063-.026-.044-.016-.09-.03-.132-.054L.402 39.944A.801.801 0 0 1 0 39.25V6.334c0-.072.01-.142.028-.21.006-.023.02-.044.028-.067.015-.042.029-.085.051-.124.015-.026.037-.047.055-.071.023-.032.044-.065.071-.093.023-.023.053-.04.079-.06.029-.024.055-.05.088-.069h.001l9.61-5.533a.802.802 0 0 1 .8 0l9.61 5.533h.002c.032.02.059.045.088.068.026.02.055.038.078.06.028.029.048.062.072.094.017.024.04.045.054.071.023.04.036.082.052.124.008.023.022.044.028.068a.809.809 0 0 1 .028.209v20.559l8.008-4.611v-10.51c0-.07.01-.141.028-.208.007-.024.02-.045.028-.068.016-.042.03-.085.052-.124.015-.026.037-.047.054-.071.024-.032.044-.065.072-.093.023-.023.052-.04.078-.06.03-.024.056-.05.088-.069h.001l9.611-5.533a.801.801 0 0 1 .8 0l9.61 5.533c.034.02.06.045.09.068.025.02.054.038.077.06.028.029.048.062.072.094.018.024.04.045.054.071.023.039.036.082.052.124.009.023.022.044.028.068zm-1.574 10.718v-9.124l-3.363 1.936-4.646 2.675v9.124l8.01-4.611zm-9.61 16.505v-9.13l-4.57 2.61-13.05 7.448v9.216l17.62-10.144zM1.602 7.719v31.068L19.22 48.93v-9.214l-9.204-5.209-.003-.002-.004-.002c-.031-.018-.057-.044-.086-.066-.025-.02-.054-.036-.076-.058l-.002-.003c-.026-.025-.044-.056-.066-.084-.02-.027-.044-.05-.06-.078l-.001-.003c-.018-.03-.029-.066-.042-.1-.013-.03-.03-.058-.038-.09v-.001c-.01-.038-.012-.078-.016-.117-.004-.03-.012-.06-.012-.09v-.002-21.481L4.965 9.654 1.602 7.72zm8.81-5.994L2.405 6.334l8.005 4.609 8.006-4.61-8.006-4.608zm4.164 28.764l4.645-2.674V7.719l-3.363 1.936-4.646 2.675v20.096l3.364-1.937zM39.243 7.164l-8.006 4.609 8.006 4.609 8.005-4.61-8.005-4.608zm-.801 10.605l-4.646-2.675-3.363-1.936v9.124l4.645 2.674 3.364 1.937v-9.124zM20.02 38.33l11.743-6.704 5.87-3.35-8-4.606-9.211 5.303-8.395 4.833 7.993 4.524z"
|
||||
fill="#FF2D20"
|
||||
fill-rule="evenodd"
|
||||
/></svg
|
||||
>
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-4 h-10 w-10 fill-current text-blue-500'
|
||||
: 'mx-auto w-8 h-8 fill-current text-blue-500'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<path
|
||||
d="M64 0C28.7 0 0 28.7 0 64s28.7 64 64 64c11.2 0 21.7-2.9 30.8-7.9L48.4 55.3v36.6h-6.8V41.8h6.8l50.5 75.8C116.4 106.2 128 86.5 128 64c0-35.3-28.7-64-64-64zm22.1 84.6l-7.5-11.3V41.8h7.5v42.8z"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10 text-green-500' : 'mx-auto w-8 h-8 text-green-500'}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
data-prefix="fab"
|
||||
data-icon="node-js"
|
||||
role="img"
|
||||
viewBox="0 0 448 512"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="M224 508c-6.7 0-13.5-1.8-19.4-5.2l-61.7-36.5c-9.2-5.2-4.7-7-1.7-8 12.3-4.3 14.8-5.2 27.9-12.7 1.4-.8 3.2-.5 4.6.4l47.4 28.1c1.7 1 4.1 1 5.7 0l184.7-106.6c1.7-1 2.8-3 2.8-5V149.3c0-2.1-1.1-4-2.9-5.1L226.8 37.7c-1.7-1-4-1-5.7 0L36.6 144.3c-1.8 1-2.9 3-2.9 5.1v213.1c0 2 1.1 4 2.9 4.9l50.6 29.2c27.5 13.7 44.3-2.4 44.3-18.7V167.5c0-3 2.4-5.3 5.4-5.3h23.4c2.9 0 5.4 2.3 5.4 5.3V378c0 36.6-20 57.6-54.7 57.6-10.7 0-19.1 0-42.5-11.6l-48.4-27.9C8.1 389.2.7 376.3.7 362.4V149.3c0-13.8 7.4-26.8 19.4-33.7L204.6 9c11.7-6.6 27.2-6.6 38.8 0l184.7 106.7c12 6.9 19.4 19.8 19.4 33.7v213.1c0 13.8-7.4 26.7-19.4 33.7L243.4 502.8c-5.9 3.4-12.6 5.2-19.4 5.2zm149.1-210.1c0-39.9-27-50.5-83.7-58-57.4-7.6-63.2-11.5-63.2-24.9 0-11.1 4.9-25.9 47.4-25.9 37.9 0 51.9 8.2 57.7 33.8.5 2.4 2.7 4.2 5.2 4.2h24c1.5 0 2.9-.6 3.9-1.7s1.5-2.6 1.4-4.1c-3.7-44.1-33-64.6-92.2-64.6-52.7 0-84.1 22.2-84.1 59.5 0 40.4 31.3 51.6 81.8 56.6 60.5 5.9 65.2 14.8 65.2 26.7 0 20.6-16.6 29.4-55.5 29.4-48.9 0-59.6-12.3-63.2-36.6-.4-2.6-2.6-4.5-5.3-4.5h-23.9c-3 0-5.3 2.4-5.3 5.3 0 31.1 16.9 68.2 97.8 68.2 58.4-.1 92-23.2 92-63.4z"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 400 400"
|
||||
>
|
||||
<g fill-rule="nonzero" transform="translate(0 50)" fill="none">
|
||||
<path
|
||||
d="M227.92099 83.45116l-13.6889 24.10141-46.8148-82.44693L23.7037 278.17052h97.3037c0 13.31084 10.61252 24.10142 23.70371 24.10142H23.70371c-8.46771 0-16.29145-4.59601-20.5246-12.05272-4.23315-7.4567-4.23272-16.64312.00114-24.0994L146.89383 13.05492c4.23415-7.45738 12.0596-12.05138 20.5284-12.05138 8.46878 0 16.29423 4.594 20.52839 12.05138l39.97037 70.39623z"
|
||||
fill="#00C58E"
|
||||
/>
|
||||
<path
|
||||
d="M331.6642 266.11981l-90.05432-158.56724-13.6889-24.10141-13.68888 24.10141-90.04445 158.56724c-4.23385 7.45629-4.23428 16.64271-.00113 24.09941 4.23314 7.4567 12.05689 12.05272 20.5246 12.05272h166.4c8.46946 0 16.29644-4.591 20.532-12.04837 4.23555-7.45736 4.23606-16.64592.00132-24.10376h.01976zM144.7111 278.17052L227.921 131.65399l83.19012 146.51653h-166.4z"
|
||||
fill="#FFF"
|
||||
/>
|
||||
<path
|
||||
d="M396.04938 290.22123c-4.23344 7.45557-12.05656 12.0507-20.52345 12.0507H311.1111c13.0912 0 23.7037-10.79057 23.7037-24.10141h40.66173L260.09877 74.98553l-18.4889 32.56704L227.921 83.45116l11.65432-20.51634c4.23416-7.45738 12.0596-12.05138 20.5284-12.05138 8.46879 0 16.29423 4.594 20.52839 12.05138l115.41728 203.185c4.23426 7.457 4.23426 16.6444 0 24.1014z"
|
||||
fill="#108775"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
15
apps/client/src/lib/components/icons/applications/PHP.svelte
Normal file
15
apps/client/src/lib/components/icons/applications/PHP.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-6 h-14 w-14 text-white'
|
||||
: 'mx-auto w-8 h-8 text-white'}
|
||||
>
|
||||
<path
|
||||
fill="#6181B6"
|
||||
d="M64 33.039c-33.74 0-61.094 13.862-61.094 30.961s27.354 30.961 61.094 30.961 61.094-13.862 61.094-30.961-27.354-30.961-61.094-30.961zm-15.897 36.993c-1.458 1.364-3.077 1.927-4.86 2.507-1.783.581-4.052.461-6.811.461h-6.253l-1.733 10h-7.301l6.515-34h14.04c4.224 0 7.305 1.215 9.242 3.432 1.937 2.217 2.519 5.364 1.747 9.337-.319 1.637-.856 3.159-1.614 4.515-.759 1.357-1.75 2.624-2.972 3.748zm21.311 2.968l2.881-14.42c.328-1.688.208-2.942-.361-3.555-.57-.614-1.782-1.025-3.635-1.025h-5.79l-3.731 19h-7.244l6.515-33h7.244l-1.732 9h6.453c4.061 0 6.861.815 8.402 2.231s2.003 3.356 1.387 6.528l-3.031 15.241h-7.358zm40.259-11.178c-.318 1.637-.856 3.133-1.613 4.488-.758 1.357-1.748 2.598-2.971 3.722-1.458 1.364-3.078 1.927-4.86 2.507-1.782.581-4.053.461-6.812.461h-6.253l-1.732 10h-7.301l6.514-34h14.041c4.224 0 7.305 1.215 9.241 3.432 1.935 2.217 2.518 5.418 1.746 9.39zM95.919 54h-5.001l-2.727 14h4.442c2.942 0 5.136-.29 6.576-1.4 1.442-1.108 2.413-2.828 2.918-5.421.484-2.491.264-4.434-.66-5.458-.925-1.024-2.774-1.721-5.548-1.721zM38.934 54h-5.002l-2.727 14h4.441c2.943 0 5.136-.29 6.577-1.4 1.441-1.108 2.413-2.828 2.917-5.421.484-2.491.264-4.434-.66-5.458s-2.772-1.721-5.546-1.721z"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-6 h-14 w-14' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<linearGradient
|
||||
id="a"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="70.252"
|
||||
y1="1237.476"
|
||||
x2="170.659"
|
||||
y2="1151.089"
|
||||
gradientTransform="matrix(.563 0 0 -.568 -29.215 707.817)"
|
||||
><stop offset="0" stop-color="#5A9FD4" /><stop
|
||||
offset="1"
|
||||
stop-color="#306998"
|
||||
/></linearGradient
|
||||
><path
|
||||
fill="url(#a)"
|
||||
d="M63.391 1.988c-4.222.02-8.252.379-11.8 1.007-10.45 1.846-12.346 5.71-12.346 12.837v9.411h24.693v3.137h-33.961c-7.176 0-13.46 4.313-15.426 12.521-2.268 9.405-2.368 15.275 0 25.096 1.755 7.311 5.947 12.519 13.124 12.519h8.491v-11.282c0-8.151 7.051-15.34 15.426-15.34h24.665c6.866 0 12.346-5.654 12.346-12.548v-23.513c0-6.693-5.646-11.72-12.346-12.837-4.244-.706-8.645-1.027-12.866-1.008zm-13.354 7.569c2.55 0 4.634 2.117 4.634 4.721 0 2.593-2.083 4.69-4.634 4.69-2.56 0-4.633-2.097-4.633-4.69-.001-2.604 2.073-4.721 4.633-4.721z"
|
||||
/><linearGradient
|
||||
id="b"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="209.474"
|
||||
y1="1098.811"
|
||||
x2="173.62"
|
||||
y2="1149.537"
|
||||
gradientTransform="matrix(.563 0 0 -.568 -29.215 707.817)"
|
||||
><stop offset="0" stop-color="#FFD43B" /><stop
|
||||
offset="1"
|
||||
stop-color="#FFE873"
|
||||
/></linearGradient
|
||||
><path
|
||||
fill="url(#b)"
|
||||
d="M91.682 28.38v10.966c0 8.5-7.208 15.655-15.426 15.655h-24.665c-6.756 0-12.346 5.783-12.346 12.549v23.515c0 6.691 5.818 10.628 12.346 12.547 7.816 2.297 15.312 2.713 24.665 0 6.216-1.801 12.346-5.423 12.346-12.547v-9.412h-24.664v-3.138h37.012c7.176 0 9.852-5.005 12.348-12.519 2.578-7.735 2.467-15.174 0-25.096-1.774-7.145-5.161-12.521-12.348-12.521h-9.268zm-13.873 59.547c2.561 0 4.634 2.097 4.634 4.692 0 2.602-2.074 4.719-4.634 4.719-2.55 0-4.633-2.117-4.633-4.719 0-2.595 2.083-4.692 4.633-4.692z"
|
||||
/><radialGradient
|
||||
id="c"
|
||||
cx="1825.678"
|
||||
cy="444.45"
|
||||
r="26.743"
|
||||
gradientTransform="matrix(0 -.24 -1.055 0 532.979 557.576)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
><stop offset="0" stop-color="#B8B8B8" stop-opacity=".498" /><stop
|
||||
offset="1"
|
||||
stop-color="#7F7F7F"
|
||||
stop-opacity="0"
|
||||
/></radialGradient
|
||||
><path
|
||||
opacity=".444"
|
||||
fill="url(#c)"
|
||||
enable-background="new"
|
||||
d="M97.309 119.597c0 3.543-14.816 6.416-33.091 6.416-18.276 0-33.092-2.873-33.092-6.416 0-3.544 14.815-6.417 33.092-6.417 18.275 0 33.091 2.872 33.091 6.417z"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-4 h-10 w-10 text-blue-500'
|
||||
: 'mx-auto w-8 h-8 text-blue-500'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<g fill="#61DAFB"
|
||||
><circle cx="64" cy="64" r="11.4" /><path
|
||||
d="M107.3 45.2c-2.2-.8-4.5-1.6-6.9-2.3.6-2.4 1.1-4.8 1.5-7.1 2.1-13.2-.2-22.5-6.6-26.1-1.9-1.1-4-1.6-6.4-1.6-7 0-15.9 5.2-24.9 13.9-9-8.7-17.9-13.9-24.9-13.9-2.4 0-4.5.5-6.4 1.6-6.4 3.7-8.7 13-6.6 26.1.4 2.3.9 4.7 1.5 7.1-2.4.7-4.7 1.4-6.9 2.3-12.5 4.8-19.3 11.4-19.3 18.8s6.9 14 19.3 18.8c2.2.8 4.5 1.6 6.9 2.3-.6 2.4-1.1 4.8-1.5 7.1-2.1 13.2.2 22.5 6.6 26.1 1.9 1.1 4 1.6 6.4 1.6 7.1 0 16-5.2 24.9-13.9 9 8.7 17.9 13.9 24.9 13.9 2.4 0 4.5-.5 6.4-1.6 6.4-3.7 8.7-13 6.6-26.1-.4-2.3-.9-4.7-1.5-7.1 2.4-.7 4.7-1.4 6.9-2.3 12.5-4.8 19.3-11.4 19.3-18.8s-6.8-14-19.3-18.8zm-14.8-30.5c4.1 2.4 5.5 9.8 3.8 20.3-.3 2.1-.8 4.3-1.4 6.6-5.2-1.2-10.7-2-16.5-2.5-3.4-4.8-6.9-9.1-10.4-13 7.4-7.3 14.9-12.3 21-12.3 1.3 0 2.5.3 3.5.9zm-11.2 59.3c-1.8 3.2-3.9 6.4-6.1 9.6-3.7.3-7.4.4-11.2.4-3.9 0-7.6-.1-11.2-.4-2.2-3.2-4.2-6.4-6-9.6-1.9-3.3-3.7-6.7-5.3-10 1.6-3.3 3.4-6.7 5.3-10 1.8-3.2 3.9-6.4 6.1-9.6 3.7-.3 7.4-.4 11.2-.4 3.9 0 7.6.1 11.2.4 2.2 3.2 4.2 6.4 6 9.6 1.9 3.3 3.7 6.7 5.3 10-1.7 3.3-3.4 6.6-5.3 10zm8.3-3.3c1.5 3.5 2.7 6.9 3.8 10.3-3.4.8-7 1.4-10.8 1.9 1.2-1.9 2.5-3.9 3.6-6 1.2-2.1 2.3-4.2 3.4-6.2zm-25.6 27.1c-2.4-2.6-4.7-5.4-6.9-8.3 2.3.1 4.6.2 6.9.2 2.3 0 4.6-.1 6.9-.2-2.2 2.9-4.5 5.7-6.9 8.3zm-18.6-15c-3.8-.5-7.4-1.1-10.8-1.9 1.1-3.3 2.3-6.8 3.8-10.3 1.1 2 2.2 4.1 3.4 6.1 1.2 2.2 2.4 4.1 3.6 6.1zm-7-25.5c-1.5-3.5-2.7-6.9-3.8-10.3 3.4-.8 7-1.4 10.8-1.9-1.2 1.9-2.5 3.9-3.6 6-1.2 2.1-2.3 4.2-3.4 6.2zm25.6-27.1c2.4 2.6 4.7 5.4 6.9 8.3-2.3-.1-4.6-.2-6.9-.2-2.3 0-4.6.1-6.9.2 2.2-2.9 4.5-5.7 6.9-8.3zm22.2 21l-3.6-6c3.8.5 7.4 1.1 10.8 1.9-1.1 3.3-2.3 6.8-3.8 10.3-1.1-2.1-2.2-4.2-3.4-6.2zm-54.5-16.2c-1.7-10.5-.3-17.9 3.8-20.3 1-.6 2.2-.9 3.5-.9 6 0 13.5 4.9 21 12.3-3.5 3.8-7 8.2-10.4 13-5.8.5-11.3 1.4-16.5 2.5-.6-2.3-1-4.5-1.4-6.6zm-24.7 29c0-4.7 5.7-9.7 15.7-13.4 2-.8 4.2-1.5 6.4-2.1 1.6 5 3.6 10.3 6 15.6-2.4 5.3-4.5 10.5-6 15.5-13.8-4-22.1-10-22.1-15.6zm28.5 49.3c-4.1-2.4-5.5-9.8-3.8-20.3.3-2.1.8-4.3 1.4-6.6 5.2 1.2 10.7 2 16.5 2.5 3.4 4.8 6.9 9.1 10.4 13-7.4 7.3-14.9 12.3-21 12.3-1.3 0-2.5-.3-3.5-.9zm60.8-20.3c1.7 10.5.3 17.9-3.8 20.3-1 .6-2.2.9-3.5.9-6 0-13.5-4.9-21-12.3 3.5-3.8 7-8.2 10.4-13 5.8-.5 11.3-1.4 16.5-2.5.6 2.3 1 4.5 1.4 6.6zm9-15.6c-2 .8-4.2 1.5-6.4 2.1-1.6-5-3.6-10.3-6-15.6 2.4-5.3 4.5-10.5 6-15.5 13.8 4 22.1 10 22.1 15.6 0 4.7-5.8 9.7-15.7 13.4z"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-4 h-10 w-10 text-white'
|
||||
: 'mx-auto w-8 h-8 text-white'}
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><g clip-path="url(#HTML5_Clip0_4)"
|
||||
><path
|
||||
d="M30.216 0L27.6454 28.7967L16.0907 32L4.56783 28.8012L2 0H30.216Z"
|
||||
fill="#E44D26"
|
||||
/><path
|
||||
d="M16.108 29.5515L25.4447 26.963L27.6415 2.35497H16.108V29.5515Z"
|
||||
fill="#F16529"
|
||||
/><path
|
||||
d="M11.1109 9.4197H16.108V5.88731H7.25053L7.33509 6.83499L8.20327 16.5692H16.108V13.0369H11.4338L11.1109 9.4197Z"
|
||||
fill="#EBEBEB"
|
||||
/><path
|
||||
d="M11.907 18.3354H8.36111L8.856 23.8818L16.0917 25.8904L16.108 25.8859V22.2108L16.0925 22.2149L12.1585 21.1527L11.907 18.3354Z"
|
||||
fill="#EBEBEB"
|
||||
/><path
|
||||
d="M16.0958 16.5692H20.4455L20.0354 21.1504L16.0958 22.2138V25.8887L23.3373 23.8817L23.3904 23.285L24.2205 13.9855L24.3067 13.0369H16.0958V16.5692Z"
|
||||
fill="white"
|
||||
/><path
|
||||
d="M16.0958 9.41105V9.41969H24.6281L24.6989 8.62572L24.8599 6.83499L24.9444 5.88731H16.0958V9.41105Z"
|
||||
fill="white"
|
||||
/></g
|
||||
><defs><clipPath id="HTML5_Clip0_4"><rect width="32" height="32" fill="white" /></clipPath></defs
|
||||
></svg
|
||||
>
|
@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 98.1 118"
|
||||
style="enable-background:new 0 0 98.1 118;"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<path
|
||||
fill="#FF3E00"
|
||||
d="M91.8,15.6C80.9-0.1,59.2-4.7,43.6,5.2L16.1,22.8C8.6,27.5,3.4,35.2,1.9,43.9c-1.3,7.3-0.2,14.8,3.3,21.3 c-2.4,3.6-4,7.6-4.7,11.8c-1.6,8.9,0.5,18.1,5.7,25.4c11,15.7,32.6,20.3,48.2,10.4l27.5-17.5c7.5-4.7,12.7-12.4,14.2-21.1 c1.3-7.3,0.2-14.8-3.3-21.3c2.4-3.6,4-7.6,4.7-11.8C99.2,32.1,97.1,22.9,91.8,15.6"
|
||||
/>
|
||||
<path
|
||||
fill="#FFFFFF"
|
||||
d="M40.9,103.9c-8.9,2.3-18.2-1.2-23.4-8.7c-3.2-4.4-4.4-9.9-3.5-15.3c0.2-0.9,0.4-1.7,0.6-2.6l0.5-1.6l1.4,1 c3.3,2.4,6.9,4.2,10.8,5.4l1,0.3l-0.1,1c-0.1,1.4,0.3,2.9,1.1,4.1c1.6,2.3,4.4,3.4,7.1,2.7c0.6-0.2,1.2-0.4,1.7-0.7L65.5,72 c1.4-0.9,2.3-2.2,2.6-3.8c0.3-1.6-0.1-3.3-1-4.6c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7l-10.5,6.7 c-1.7,1.1-3.6,1.9-5.6,2.4c-8.9,2.3-18.2-1.2-23.4-8.7c-3.1-4.4-4.4-9.9-3.4-15.3c0.9-5.2,4.1-9.9,8.6-12.7l27.5-17.5 c1.7-1.1,3.6-1.9,5.6-2.5c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.2,0.9-0.4,1.7-0.7,2.6l-0.5,1.6l-1.4-1 c-3.3-2.4-6.9-4.2-10.8-5.4l-1-0.3l0.1-1c0.1-1.4-0.3-2.9-1.1-4.1c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7L32.4,46.1 c-1.4,0.9-2.3,2.2-2.6,3.8s0.1,3.3,1,4.6c1.6,2.3,4.4,3.3,7.1,2.6c0.6-0.2,1.2-0.4,1.7-0.7l10.5-6.7c1.7-1.1,3.6-1.9,5.6-2.5 c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.9,5.2-4.1,9.9-8.6,12.7l-27.5,17.5C44.8,102.5,42.9,103.3,40.9,103.9"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-4 h-10 w-10 text-green-500'
|
||||
: 'mx-auto w-8 h-8 text-green-500'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<path
|
||||
d="m-2.3125e-8 8.9337 49.854 0.1586 14.167 24.47 14.432-24.47 49.547-0.1577-63.834 110.14zm126.98 0.6374-24.36 0.0207-38.476 66.052-38.453-66.052-24.749-0.0194 63.211 107.89zm-25.149-0.008-22.745 0.16758l-15.053 24.647-14.817-24.647-22.794-0.1679 37.731 64.476zM25.997 9.3929l23.002 0.0087M25.997 9.3929l23.002 0.0087"
|
||||
fill="none"
|
||||
/><path
|
||||
d="m25.997 9.3929 23.002 0.0087l15.036 24.958 14.983-24.956 22.982-0.0057-37.85 65.655z"
|
||||
fill="#35495e"
|
||||
/><path
|
||||
d="m0.91068 9.5686 25.066-0.1711 38.151 65.658 37.852-65.654 25.11 0.0263-62.966 108.06z"
|
||||
fill="#41b883"
|
||||
/>
|
||||
</svg>
|
20
apps/client/src/lib/components/icons/applications/index.ts
Normal file
20
apps/client/src/lib/components/icons/applications/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
//@ts-nocheck
|
||||
export { default as Rust } from './Rust.svelte';
|
||||
export { default as Nodejs } from './Nodejs.svelte';
|
||||
export { default as React } from './React.svelte';
|
||||
export { default as Svelte } from './Svelte.svelte';
|
||||
export { default as Vuejs } from './Vuejs.svelte';
|
||||
export { default as Php } from './PHP.svelte';
|
||||
export { default as Python } from './Python.svelte';
|
||||
export { default as Static } from './Static.svelte';
|
||||
export { default as Nestjs } from './Nestjs.svelte';
|
||||
export { default as Nuxtjs } from './Nuxtjs.svelte';
|
||||
export { default as Nextjs } from './Nextjs.svelte';
|
||||
export { default as Gatsby } from './Gatsby.svelte';
|
||||
export { default as Docker } from './Docker.svelte';
|
||||
export { default as Astro } from './Astro.svelte';
|
||||
export { default as Eleventy } from './Eleventy.svelte';
|
||||
export { default as Deno } from './Deno.svelte';
|
||||
export { default as Laravel } from './Laravel.svelte';
|
||||
export { default as Heroku } from './Heroku.svelte';
|
||||
export { default as Compose } from './Compose.svelte';
|
@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 9 8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path d="m0 7h1v1h-1z" fill="#f00" /><path
|
||||
d="m0 0h1v7h-1zm2 0h1v8h-1zm2 0h1v8h-1zm2 0h1v8h-1zm2 3.25h1v1.5h-1z"
|
||||
fill="#fc0"
|
||||
/></svg
|
||||
>
|
@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-5 h-10 w-10 fill-current text-red-500'
|
||||
: 'mx-auto w-8 h-8 text-red-500'}
|
||||
id="CouchDB"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 128 128"
|
||||
><g id="original"
|
||||
><path
|
||||
d="M101.4,77.2c0,5-2.7,7.5-7.6,7.7H33.9c-4.9,0-7.6-2.5-7.6-7.7,0-5,2.7-7.5,7.6-7.7H94.1C99,69.7,101.4,72.2,101.4,77.2ZM94.1,88.7H33.9c-4.9,0-7.6,2.4-7.6,7.7,0,5,2.7,7.4,7.6,7.7H94.1c4.9,0,7.6-2.5,7.6-7.7C101.4,91.1,99,88.7,94.1,88.7Zm18.6-42.1h0c-4.9,0-7.6,2.5-7.6,7.4V96.1c0,5,2.7,7.5,7.6,7.7h0c7.4-.2,11.3-7.7,11.3-22.9V62C124,51.8,120.1,46.8,112.7,46.6Zm-97.4,0h0C7.9,46.8,4,51.8,4,62V80.9c0,15.2,3.9,22.7,11.3,22.9h0c4.9,0,7.6-2.4,7.6-7.7V54.3C22.7,49.3,20.2,46.8,15.3,46.6Zm97.4-3.8c0-12.7-6.6-18.7-18.6-18.9H33.9c-12.2.2-18.6,6.5-18.6,18.9h0c7.4,0,11.3,4,11.3,11.5s3.9,11.4,11.3,11.4H90.4c7.3,0,11.3-3.9,11.3-11.4-.3-7.7,3.9-11.2,11-11.5Z"
|
||||
/></g
|
||||
></svg
|
||||
>
|
@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import * as Icons from '$lib/components/icons/databases';
|
||||
export let type: any;
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
{#if type === 'mysql'}
|
||||
<Icons.MySQL {isAbsolute} />
|
||||
{:else if type === 'postgresql'}
|
||||
<Icons.PostgreSQL {isAbsolute} />
|
||||
{:else if type === 'mongodb'}
|
||||
<Icons.MongoDB {isAbsolute} />
|
||||
{:else if type === 'mariadb'}
|
||||
<Icons.MariaDB {isAbsolute} />
|
||||
{:else if type === 'redis'}
|
||||
<Icons.Redis {isAbsolute} />
|
||||
{:else if type === 'couchdb'}
|
||||
<Icons.CouchDB {isAbsolute} />
|
||||
{:else if type === 'edgedb'}
|
||||
<Icons.EdgeDB {isAbsolute} />
|
||||
{/if}
|
22
apps/client/src/lib/components/icons/databases/EdgeDB.svelte
Normal file
22
apps/client/src/lib/components/icons/databases/EdgeDB.svelte
Normal file
@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-8 h-16 w-16' : 'mx-auto w-12 h-12'}
|
||||
width="88"
|
||||
fill="#1F8AED"
|
||||
height="101"
|
||||
viewBox="0 -15 88 101"
|
||||
><path
|
||||
class="pageNav_logoBar__2v4ah"
|
||||
style="transform-origin:center 35.5px"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M55.1436 71H58.1436V0H55.1436V71Z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M74.5362 35.3047C74.5362 41.3776 72.1013 42.4662 69.3799 42.4662H63.5935V28.1432H69.3799C72.1013 28.1432 74.5362 29.2318 74.5362 35.3047V35.3047ZM71.5862 35.3047C71.5862 31.0651 70.2971 30.8646 68.4352 30.8646H66.6305V39.7448H68.4352C70.2971 39.7448 71.5862 39.5443 71.5862 35.3047V35.3047ZM40.9348 42.4662V28.1432H50.0442V30.8646H43.9713V33.7865H48.5546V36.4792H43.9713V39.7448H50.0442V42.4662H40.9348ZM80.6092 36.1068V39.7448H83.13C84.7055 39.7448 85.1066 38.7135 85.1066 37.9401C85.1066 37.3385 84.8201 36.1068 82.6717 36.1068H80.6092ZM80.6092 30.8646V33.5859H82.6717C83.8462 33.5859 84.5337 33.0703 84.5337 32.2109C84.5337 31.3516 83.8462 30.8646 82.6717 30.8646H80.6092ZM77.5732 28.1432H83.4169C86.482 28.1432 87.3987 30.2917 87.3987 31.8385C87.3987 33.2708 86.482 34.3021 85.8518 34.5885C87.6851 35.4766 88.0002 37.2813 88.0002 38.1979C88.0002 39.401 87.3987 42.4662 83.4169 42.4662H77.5732V28.1432ZM23.4899 35.3047C23.4899 41.3776 21.055 42.4662 18.3337 42.4662H12.5472V28.1432H18.3337C21.055 28.1432 23.4899 29.2318 23.4899 35.3047V35.3047ZM32.4272 39.8594C33.974 39.8594 34.7761 39.3438 35.0626 39V37.4245H32.599V34.9609H37.4975V40.6615C37.0678 41.3203 34.7188 42.6094 32.5704 42.6094C29.047 42.6094 26.0678 41.2344 26.0678 35.1615C26.0678 29.0885 29.0756 28 31.797 28C36.0652 28 37.1251 30.2344 37.4688 32.2109L34.948 32.7839C34.8048 31.8672 34.0027 30.7214 32.1694 30.7214C30.3074 30.7214 29.0183 30.9219 29.0183 35.1615C29.0183 39.401 30.3647 39.8594 32.4272 39.8594V39.8594ZM20.539 35.3047C20.539 31.0651 19.2499 30.8646 17.3879 30.8646H15.5833V39.7448H17.3879C19.2499 39.7448 20.539 39.5443 20.539 35.3047V35.3047ZM0 42.4662V28.1432H9.10938V30.8646H3.03646V33.7865H7.61979V36.4792H3.03646V39.7448H9.10938V42.4662H0Z"
|
||||
/></svg
|
||||
>
|
@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 309.88 252.72"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-12 w-12 ' : 'mx-auto w-8 h-8'}
|
||||
>
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M316,10.05a4.2,4.2,0,0,0-2.84-1c-2.84,0-6.5,1.92-8.46,3l-.79.4a26.81,26.81,0,0,1-10.57,2.66c-3.76.12-7,.34-11.22.77-25,2.58-36.15,21.74-46.89,40.27-5.84,10.08-11.88,20.5-20.16,28.57a55.71,55.71,0,0,1-5.46,4.63c-8.57,6.39-19.33,10.9-27.74,14.12-8.07,3.08-16.86,5.85-25.37,8.53-7.78,2.45-15.14,4.76-21.9,7.28-3.05,1.13-5.64,2-7.93,2.76-6.15,2-10.6,3.53-17.08,8-2.53,1.73-5.07,3.6-6.8,5a71.26,71.26,0,0,0-13.54,14.27A84.81,84.81,0,0,1,77.88,163c-1.36,1.34-3.8,2-7.43,2-4.27,0-9.43-.88-14.91-1.81s-11.46-2-16.46-2c-4.07,0-7.17.66-9.5,2,0,0-3.9,2.28-5.56,5.23l1.62.73a33.56,33.56,0,0,1,6.93,5,33.68,33.68,0,0,0,7.19,5.12A6.37,6.37,0,0,1,42,180.72c-.69,1-1.69,2.29-2.74,3.67-5.77,7.55-9.13,12.32-7.2,14.92a6,6,0,0,0,3,.68c12.59,0,19.34-3.27,27.9-7.41,2.47-1.2,5-2.44,8-3.7,5-2.17,10.38-5.63,16.08-9.29,7.55-4.85,15.36-9.87,22.92-12.3a62.3,62.3,0,0,1,19.23-2.7c8,0,16.42,1.07,24.54,2.11,6.06.78,12.32,1.58,18.47,2,2.39.14,4.6.21,6.76.21a78.48,78.48,0,0,0,8.61-.45l.68-.24c4.32-2.65,6.34-8.34,8.29-13.84,1.26-3.54,2.32-6.72,4-8.74a2.06,2.06,0,0,1,.33-.27.4.4,0,0,1,.49.08.25.25,0,0,1,0,.16c-1,21.51-9.67,35.16-18.42,47.3L177,199.14s8.18,0,12.84-1.8c17-5.08,29.84-16.28,39.18-34.14a144.39,144.39,0,0,0,6.16-14.09c.16-.4,1.64-1.14,1.49.93,0,.61-.08,1.29-.13,2h0c0,.42-.06.85-.08,1.28-.25,3-1,9.34-1,9.34l5.25-2.81c12.66-8,22.42-24.14,29.82-49.25,3.09-10.46,5.34-20.85,7.33-30,2.38-11,4.43-20.43,6.78-24.09,3.69-5.74,9.32-9.62,14.77-13.39.75-.51,1.49-1,2.22-1.54,6.86-4.81,13.67-10.36,15.16-20.71l0-.23C317.93,12.92,317,11,316,10.05Z"
|
||||
transform="translate(-7.45 -9.1)"
|
||||
/>
|
||||
</svg>
|
@ -0,0 +1,90 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
>
|
||||
<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>
|
17
apps/client/src/lib/components/icons/databases/MySQL.svelte
Normal file
17
apps/client/src/lib/components/icons/databases/MySQL.svelte
Normal file
@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 25.6 25.6"
|
||||
><path
|
||||
d="M179.076 94.886c-3.568-.1-6.336.268-8.656 1.25-.668.27-1.74.27-1.828 1.116.357.355.4.936.713 1.428.535.893 1.473 2.096 2.32 2.72l2.855 2.053c1.74 1.07 3.703 1.695 5.398 2.766.982.625 1.963 1.428 2.945 2.098.5.357.803.938 1.428 1.16v-.135c-.312-.4-.402-.98-.713-1.428l-1.34-1.293c-1.293-1.74-2.9-3.258-4.64-4.506-1.428-.982-4.55-2.32-5.13-3.97l-.088-.1c.98-.1 2.14-.447 3.078-.715 1.518-.4 2.9-.312 4.46-.713l2.143-.625v-.4c-.803-.803-1.383-1.874-2.23-2.632-2.275-1.963-4.775-3.882-7.363-5.488-1.383-.892-3.168-1.473-4.64-2.23-.537-.268-1.428-.402-1.74-.848-.805-.98-1.25-2.275-1.83-3.436l-3.658-7.763c-.803-1.74-1.295-3.48-2.275-5.086-4.596-7.585-9.594-12.18-17.268-16.687-1.65-.937-3.613-1.34-5.7-1.83l-3.346-.18c-.715-.312-1.428-1.16-2.053-1.562-2.543-1.606-9.102-5.086-10.977-.5-1.205 2.9 1.785 5.755 2.8 7.228.76 1.026 1.74 2.186 2.277 3.346.3.758.4 1.562.713 2.365.713 1.963 1.383 4.15 2.32 5.98.5.937 1.025 1.92 1.65 2.767.357.5.982.714 1.115 1.517-.625.893-.668 2.23-1.025 3.347-1.607 5.042-.982 11.288 1.293 15 .715 1.115 2.4 3.57 4.686 2.632 2.008-.803 1.56-3.346 2.14-5.577.135-.535.045-.892.312-1.25v.1l1.83 3.703c1.383 2.186 3.793 4.462 5.8 5.98 1.07.803 1.918 2.187 3.256 2.677v-.135h-.088c-.268-.4-.67-.58-1.027-.892-.803-.803-1.695-1.785-2.32-2.677-1.873-2.498-3.523-5.265-4.996-8.12-.715-1.383-1.34-2.9-1.918-4.283-.27-.536-.27-1.34-.715-1.606-.67.98-1.65 1.83-2.143 3.034-.848 1.918-.936 4.283-1.248 6.737-.18.045-.1 0-.18.1-1.426-.356-1.918-1.83-2.453-3.078-1.338-3.168-1.562-8.254-.402-11.913.312-.937 1.652-3.882 1.117-4.774-.27-.848-1.16-1.338-1.652-2.008-.58-.848-1.203-1.918-1.605-2.855-1.07-2.5-1.605-5.265-2.766-7.764-.537-1.16-1.473-2.365-2.232-3.435-.848-1.205-1.783-2.053-2.453-3.48-.223-.5-.535-1.294-.178-1.83.088-.357.268-.5.623-.58.58-.5 2.232.134 2.812.4 1.65.67 3.033 1.294 4.416 2.23.625.446 1.295 1.294 2.098 1.518h.938c1.428.312 3.033.1 4.37.5 2.365.76 4.506 1.874 6.426 3.08 5.844 3.703 10.664 8.968 13.92 15.26.535 1.026.758 1.963 1.25 3.034.938 2.187 2.098 4.417 3.033 6.56.938 2.097 1.83 4.24 3.168 5.98.67.937 3.346 1.427 4.55 1.918.893.4 2.275.76 3.08 1.25 1.516.937 3.033 2.008 4.46 3.034.713.534 2.945 1.65 3.078 2.54zm-45.5-38.772a7.09 7.09 0 0 0-1.828.223v.1h.088c.357.714.982 1.205 1.428 1.83l1.027 2.142.088-.1c.625-.446.938-1.16.938-2.23-.268-.312-.312-.625-.535-.937-.268-.446-.848-.67-1.206-1.026z"
|
||||
transform="matrix(.390229 0 0 .38781 -46.300037 -16.856717)"
|
||||
fill-rule="evenodd"
|
||||
fill="#00678c"
|
||||
/></svg
|
||||
>
|
@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 432.071 445.383"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<g id="orginal" style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;" />
|
||||
<g
|
||||
id="Layer_x0020_3"
|
||||
style="fill-rule:nonzero;clip-rule:nonzero;fill:none;stroke:#FFFFFF;stroke-width:12.4651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;"
|
||||
>
|
||||
<path
|
||||
style="fill:#000000;stroke:#000000;stroke-width:37.3953;stroke-linecap:butt;stroke-linejoin:miter;"
|
||||
d="M323.205,324.227c2.833-23.601,1.984-27.062,19.563-23.239l4.463,0.392c13.517,0.615,31.199-2.174,41.587-7c22.362-10.376,35.622-27.7,13.572-23.148c-50.297,10.376-53.755-6.655-53.755-6.655c53.111-78.803,75.313-178.836,56.149-203.322 C352.514-5.534,262.036,26.049,260.522,26.869l-0.482,0.089c-9.938-2.062-21.06-3.294-33.554-3.496c-22.761-0.374-40.032,5.967-53.133,15.904c0,0-161.408-66.498-153.899,83.628c1.597,31.936,45.777,241.655,98.47,178.31 c19.259-23.163,37.871-42.748,37.871-42.748c9.242,6.14,20.307,9.272,31.912,8.147l0.897-0.765c-0.281,2.876-0.157,5.689,0.359,9.019c-13.572,15.167-9.584,17.83-36.723,23.416c-27.457,5.659-11.326,15.734-0.797,18.367c12.768,3.193,42.305,7.716,62.268-20.224 l-0.795,3.188c5.325,4.26,4.965,30.619,5.72,49.452c0.756,18.834,2.017,36.409,5.856,46.771c3.839,10.36,8.369,37.05,44.036,29.406c29.809-6.388,52.6-15.582,54.677-101.107"
|
||||
/>
|
||||
<path
|
||||
style="fill:#336791;stroke:none;"
|
||||
d="M402.395,271.23c-50.302,10.376-53.76-6.655-53.76-6.655c53.111-78.808,75.313-178.843,56.153-203.326c-52.27-66.785-142.752-35.2-144.262-34.38l-0.486,0.087c-9.938-2.063-21.06-3.292-33.56-3.496c-22.761-0.373-40.026,5.967-53.127,15.902 c0,0-161.411-66.495-153.904,83.63c1.597,31.938,45.776,241.657,98.471,178.312c19.26-23.163,37.869-42.748,37.869-42.748c9.243,6.14,20.308,9.272,31.908,8.147l0.901-0.765c-0.28,2.876-0.152,5.689,0.361,9.019c-13.575,15.167-9.586,17.83-36.723,23.416 c-27.459,5.659-11.328,15.734-0.796,18.367c12.768,3.193,42.307,7.716,62.266-20.224l-0.796,3.188c5.319,4.26,9.054,27.711,8.428,48.969c-0.626,21.259-1.044,35.854,3.147,47.254c4.191,11.4,8.368,37.05,44.042,29.406c29.809-6.388,45.256-22.942,47.405-50.555 c1.525-19.631,4.976-16.729,5.194-34.28l2.768-8.309c3.192-26.611,0.507-35.196,18.872-31.203l4.463,0.392c13.517,0.615,31.208-2.174,41.591-7c22.358-10.376,35.618-27.7,13.573-23.148z"
|
||||
/>
|
||||
<path
|
||||
d="M215.866,286.484c-1.385,49.516,0.348,99.377,5.193,111.495c4.848,12.118,15.223,35.688,50.9,28.045c29.806-6.39,40.651-18.756,45.357-46.051c3.466-20.082,10.148-75.854,11.005-87.281"
|
||||
/>
|
||||
<path
|
||||
d="M173.104,38.256c0,0-161.521-66.016-154.012,84.109c1.597,31.938,45.779,241.664,98.473,178.316c19.256-23.166,36.671-41.335,36.671-41.335"
|
||||
/>
|
||||
<path
|
||||
d="M260.349,26.207c-5.591,1.753,89.848-34.889,144.087,34.417c19.159,24.484-3.043,124.519-56.153,203.329"
|
||||
/>
|
||||
<path
|
||||
style="stroke-linejoin:bevel;"
|
||||
d="M348.282,263.953c0,0,3.461,17.036,53.764,6.653c22.04-4.552,8.776,12.774-13.577,23.155c-18.345,8.514-59.474,10.696-60.146-1.069c-1.729-30.355,21.647-21.133,19.96-28.739c-1.525-6.85-11.979-13.573-18.894-30.338 c-6.037-14.633-82.796-126.849,21.287-110.183c3.813-0.789-27.146-99.002-124.553-100.599c-97.385-1.597-94.19,119.762-94.19,119.762"
|
||||
/>
|
||||
<path
|
||||
d="M188.604,274.334c-13.577,15.166-9.584,17.829-36.723,23.417c-27.459,5.66-11.326,15.733-0.797,18.365c12.768,3.195,42.307,7.718,62.266-20.229c6.078-8.509-0.036-22.086-8.385-25.547c-4.034-1.671-9.428-3.765-16.361,3.994z"
|
||||
/>
|
||||
<path
|
||||
d="M187.715,274.069c-1.368-8.917,2.93-19.528,7.536-31.942c6.922-18.626,22.893-37.255,10.117-96.339c-9.523-44.029-73.396-9.163-73.436-3.193c-0.039,5.968,2.889,30.26-1.067,58.548c-5.162,36.913,23.488,68.132,56.479,64.938"
|
||||
/>
|
||||
<path
|
||||
style="fill:#FFFFFF;stroke-width:4.155;stroke-linecap:butt;stroke-linejoin:miter;"
|
||||
d="M172.517,141.7c-0.288,2.039,3.733,7.48,8.976,8.207c5.234,0.73,9.714-3.522,9.998-5.559c0.284-2.039-3.732-4.285-8.977-5.015c-5.237-0.731-9.719,0.333-9.996,2.367z"
|
||||
/>
|
||||
<path
|
||||
style="fill:#FFFFFF;stroke-width:2.0775;stroke-linecap:butt;stroke-linejoin:miter;"
|
||||
d="M331.941,137.543c0.284,2.039-3.732,7.48-8.976,8.207c-5.238,0.73-9.718-3.522-10.005-5.559c-0.277-2.039,3.74-4.285,8.979-5.015c5.239-0.73,9.718,0.333,10.002,2.368z"
|
||||
/>
|
||||
<path
|
||||
d="M350.676,123.432c0.863,15.994-3.445,26.888-3.988,43.914c-0.804,24.748,11.799,53.074-7.191,81.435"
|
||||
/>
|
||||
<path style="stroke-width:3;" d="M0,60.232" />
|
||||
</g>
|
||||
</svg>
|
34
apps/client/src/lib/components/icons/databases/Redis.svelte
Normal file
34
apps/client/src/lib/components/icons/databases/Redis.svelte
Normal file
@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 32 32"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
><defs
|
||||
><path
|
||||
id="a"
|
||||
d="m45.536 38.764c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.813s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z"
|
||||
/><path
|
||||
id="b"
|
||||
d="m45.536 28.733c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.935c2.332-.837 3.14-.867 5.126-.14s12.35 4.853 14.312 5.57 2.037 1.31.024 2.36z"
|
||||
/></defs
|
||||
><g transform="matrix(.848327 0 0 .848327 -7.883573 -9.449691)"
|
||||
><use fill="#a41e11" xlink:href="#a" /><path
|
||||
d="m45.536 34.95c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.936c2.332-.836 3.14-.867 5.126-.14s12.35 4.852 14.31 5.582 2.037 1.31.024 2.36z"
|
||||
fill="#d82c20"
|
||||
/><use fill="#a41e11" xlink:href="#a" y="-6.218" /><use fill="#d82c20" xlink:href="#b" /><path
|
||||
d="m45.536 26.098c-2.013 1.05-12.44 5.337-14.66 6.495s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.815s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z"
|
||||
fill="#a41e11"
|
||||
/><use fill="#d82c20" xlink:href="#b" y="-6.449" /><g fill="#fff"
|
||||
><path
|
||||
d="m29.096 20.712-1.182-1.965-3.774-.34 2.816-1.016-.845-1.56 2.636 1.03 2.486-.814-.672 1.612 2.534.95-3.268.34zm-6.296 3.912 8.74-1.342-2.64 3.872z"
|
||||
/><ellipse cx="20.444" cy="21.402" rx="4.672" ry="1.811" /></g
|
||||
><path d="m42.132 21.138-5.17 2.042-.004-4.087z" fill="#7a0c00" /><path
|
||||
d="m36.963 23.18-.56.22-5.166-2.042 5.723-2.264z"
|
||||
fill="#ad2115"
|
||||
/></g
|
||||
></svg
|
||||
>
|
11
apps/client/src/lib/components/icons/databases/index.ts
Normal file
11
apps/client/src/lib/components/icons/databases/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
//@ts-nocheck
|
||||
export { default as Clickhouse } from './Clickhouse.svelte';
|
||||
export { default as CouchDB } from './CouchDB.svelte';
|
||||
export { default as MariaDB } from './MariaDB.svelte';
|
||||
export { default as MongoDB } from './MongoDB.svelte';
|
||||
export { default as MySQL } from './MySQL.svelte';
|
||||
export { default as PostgreSQL } from './PostgreSQL.svelte';
|
||||
export { default as Redis } from './Redis.svelte';
|
||||
export { default as EdgeDB } from './EdgeDB.svelte';
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
<script>
|
||||
export let isAbsolute=false;
|
||||
</script>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-2 h-12 w-12 text-sky-500' : 'mx-auto w-8 h-8 text-sky-500'}
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M22 12.54c-1.804 -.345 -2.701 -1.08 -3.523 -2.94c-.487 .696 -1.102 1.568 -.92 2.4c.028 .238 -.32 1.002 -.557 1h-14c0 5.208 3.164 7 6.196 7c4.124 .022 7.828 -1.376 9.854 -5c1.146 -.101 2.296 -1.505 2.95 -2.46z"
|
||||
/>
|
||||
<path d="M5 10h3v3h-3z" />
|
||||
<path d="M8 10h3v3h-3z" />
|
||||
<path d="M11 10h3v3h-3z" />
|
||||
<path d="M8 7h3v3h-3z" />
|
||||
<path d="M11 7h3v3h-3z" />
|
||||
<path d="M11 4h3v3h-3z" />
|
||||
<path d="M4.571 18c1.5 0 2.047 -.074 2.958 -.78" />
|
||||
<line x1="10" y1="16" x2="10" y2="16.01" />
|
||||
</svg>
|
@ -0,0 +1,16 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="absolute top-0 left-9 -m-2 h-6 w-6 text-sky-500 rotate-45"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="3"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<line x1="12" y1="18" x2="12.01" y2="18" />
|
||||
<path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
|
||||
<path d="M6.343 12.343a8 8 0 0 1 11.314 0" />
|
||||
<path d="M3.515 9.515c4.686 -4.687 12.284 -4.687 17 0" />
|
||||
</svg>
|
After Width: | Height: | Size: 505 B |
@ -0,0 +1,2 @@
|
||||
export { default as LocalDocker } from './LocalDocker.svelte';
|
||||
export { default as RemoteDocker } from './RemoteDocker.svelte';
|
6
apps/client/src/lib/components/icons/index.ts
Normal file
6
apps/client/src/lib/components/icons/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as RemoteLink } from './RemoteLink.svelte';
|
||||
export { default as Delete } from './Delete.svelte';
|
||||
export * as Applications from './applications';
|
||||
export * as Sources from './sources';
|
||||
export * as Destinations from './destinations';
|
||||
export * as Databases from './databases';
|
@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
export let type: string;
|
||||
export let isAbsolute = false;
|
||||
let fallback = '/icons/default.png';
|
||||
const handleError = (ev: { target: { src: string } }) => (ev.target.src = fallback);
|
||||
let extension = 'png';
|
||||
let svgs = [
|
||||
'pocketbase',
|
||||
'gitea',
|
||||
'languagetool',
|
||||
'meilisearch',
|
||||
'n8n',
|
||||
'glitchtip',
|
||||
'searxng',
|
||||
'umami',
|
||||
'uptimekuma',
|
||||
'vaultwarden',
|
||||
'weblate',
|
||||
'wordpress'
|
||||
];
|
||||
|
||||
const name: any =
|
||||
type &&
|
||||
(type[0].toUpperCase() + type.substring(1).toLowerCase())
|
||||
.replaceAll('.', '')
|
||||
.replaceAll(' ', '')
|
||||
.split('-')[0]
|
||||
.toLowerCase();
|
||||
|
||||
if (svgs.includes(name)) {
|
||||
extension = 'svg';
|
||||
}
|
||||
|
||||
function generateClass() {
|
||||
switch (name) {
|
||||
case 'n8n':
|
||||
if (isAbsolute) {
|
||||
return 'w-12 h-12 absolute -m-9 -mt-12';
|
||||
}
|
||||
return 'w-12 h-12 -mt-3';
|
||||
case 'weblate':
|
||||
if (isAbsolute) {
|
||||
return 'w-12 h-12 absolute -m-9 -mt-12';
|
||||
}
|
||||
return 'w-12 h-12 -mt-3';
|
||||
default:
|
||||
return isAbsolute ? 'w-10 h-10 absolute -m-4 -mt-9 left-0' : 'w-10 h-10';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if name}
|
||||
<img
|
||||
class={generateClass()}
|
||||
src={`/icons/${name}.${extension}`}
|
||||
on:error={handleError}
|
||||
alt={`Icon of ${name}`}
|
||||
/>
|
||||
{/if}
|
15
apps/client/src/lib/components/icons/sources/Github.svelte
Normal file
15
apps/client/src/lib/components/icons/sources/Github.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
export let small = false;
|
||||
</script>
|
||||
|
||||
<svg viewBox="0 0 128 128" class={small ? 'h-6 w-6' : 'h-10 w-10'}>
|
||||
<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>
|
25
apps/client/src/lib/components/icons/sources/Gitlab.svelte
Normal file
25
apps/client/src/lib/components/icons/sources/Gitlab.svelte
Normal file
@ -0,0 +1,25 @@
|
||||
<script>
|
||||
export let small = false;
|
||||
</script>
|
||||
|
||||
<svg viewBox="0 0 128 128" class={small ? 'h-6 w-6' : 'h-10 w-10'}>
|
||||
<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>
|
2
apps/client/src/lib/components/icons/sources/index.ts
Normal file
2
apps/client/src/lib/components/icons/sources/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as GitHub } from './Github.svelte';
|
||||
export { default as GitLab } from './Gitlab.svelte';
|
7
apps/client/src/lib/dayjs.ts
Normal file
7
apps/client/src/lib/dayjs.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc.js';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime.js';
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
export { dayjs as day };
|
173
apps/client/src/lib/store.ts
Normal file
173
apps/client/src/lib/store.ts
Normal file
@ -0,0 +1,173 @@
|
||||
import { writable, readable, type Writable } from 'svelte/store';
|
||||
import superjson from 'superjson';
|
||||
import type { AppRouter } from 'server/src/trpc';
|
||||
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||
import { browser, dev } from '$app/environment';
|
||||
import Cookies from 'js-cookie';
|
||||
import cuid from 'cuid';
|
||||
|
||||
export const serverBaseUrl = dev ? `http://${browser && window.location.hostname}:2022` : '';
|
||||
export let token: string = Cookies.get('token') || '';
|
||||
export const trpc = createTRPCProxyClient<AppRouter>({
|
||||
transformer: superjson,
|
||||
links: [
|
||||
httpBatchLink({
|
||||
url: `${serverBaseUrl}/trpc`,
|
||||
headers() {
|
||||
return {
|
||||
Authorization: token
|
||||
};
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
export const disabledButton: Writable<boolean> = writable(false);
|
||||
export const location: Writable<null | string> = writable(null)
|
||||
interface AppSession {
|
||||
isRegistrationEnabled: boolean;
|
||||
token?: string;
|
||||
ipv4: string | null;
|
||||
ipv6: string | null;
|
||||
version: string | null;
|
||||
userId: string | null;
|
||||
teamId: string | null;
|
||||
permission: string;
|
||||
isAdmin: boolean;
|
||||
whiteLabeled: boolean;
|
||||
whiteLabeledDetails: {
|
||||
icon: string | null;
|
||||
};
|
||||
tokens: {
|
||||
github: string | null;
|
||||
gitlab: string | null;
|
||||
};
|
||||
pendingInvitations: Array<any>;
|
||||
}
|
||||
|
||||
export const appSession: Writable<AppSession> = writable({
|
||||
isRegistrationEnabled: false,
|
||||
ipv4: null,
|
||||
ipv6: null,
|
||||
version: null,
|
||||
userId: null,
|
||||
teamId: null,
|
||||
permission: 'read',
|
||||
isAdmin: false,
|
||||
whiteLabeled: false,
|
||||
whiteLabeledDetails: {
|
||||
icon: null
|
||||
},
|
||||
tokens: {
|
||||
github: null,
|
||||
gitlab: null
|
||||
},
|
||||
pendingInvitations: []
|
||||
});
|
||||
|
||||
interface AddToast {
|
||||
type?: 'info' | 'success' | 'error';
|
||||
message: string;
|
||||
timeout?: number | undefined;
|
||||
}
|
||||
export const toasts: any = writable([]);
|
||||
|
||||
export const dismissToast = (id: string) => {
|
||||
toasts.update((all: any) => all.filter((t: any) => t.id !== id));
|
||||
};
|
||||
export const pauseToast = (id: string) => {
|
||||
toasts.update((all: any) => {
|
||||
const index = all.findIndex((t: any) => t.id === id);
|
||||
if (index > -1) clearTimeout(all[index].timeoutInterval);
|
||||
return all;
|
||||
});
|
||||
};
|
||||
export const resumeToast = (id: string) => {
|
||||
toasts.update((all: any) => {
|
||||
const index = all.findIndex((t: any) => t.id === id);
|
||||
if (index > -1) {
|
||||
all[index].timeoutInterval = setTimeout(() => {
|
||||
dismissToast(id);
|
||||
}, all[index].timeout);
|
||||
}
|
||||
return all;
|
||||
});
|
||||
};
|
||||
|
||||
export const addToast = (toast: AddToast) => {
|
||||
const id = cuid();
|
||||
const defaults = {
|
||||
id,
|
||||
type: 'info',
|
||||
timeout: 2000
|
||||
};
|
||||
let t: any = { ...defaults, ...toast };
|
||||
if (t.timeout) t.timeoutInterval = setTimeout(() => dismissToast(id), t.timeout);
|
||||
toasts.update((all: any) => [t, ...all]);
|
||||
};
|
||||
|
||||
export const features = readable({
|
||||
beta: browser && window.localStorage.getItem('beta') === 'true',
|
||||
latestVersion: browser && window.localStorage.getItem('latestVersion')
|
||||
});
|
||||
|
||||
export const updateLoading: Writable<boolean> = writable(false);
|
||||
export const isUpdateAvailable: Writable<boolean> = writable(false);
|
||||
export const latestVersion: Writable<string> = writable('latest');
|
||||
export const loginEmail: Writable<string | undefined> = writable();
|
||||
export const search: any = writable('');
|
||||
|
||||
export const isDeploymentEnabled: Writable<boolean> = writable(false);
|
||||
export const status: Writable<any> = writable({
|
||||
application: {
|
||||
statuses: [],
|
||||
overallStatus: 'stopped',
|
||||
loading: false,
|
||||
restarting: false,
|
||||
initialLoading: true
|
||||
},
|
||||
service: {
|
||||
statuses: [],
|
||||
overallStatus: 'stopped',
|
||||
loading: false,
|
||||
startup: {},
|
||||
initialLoading: true
|
||||
},
|
||||
database: {
|
||||
isRunning: false,
|
||||
isExited: false,
|
||||
loading: false,
|
||||
initialLoading: true,
|
||||
isPublic: false
|
||||
}
|
||||
});
|
||||
|
||||
export function checkIfDeploymentEnabledApplications(isAdmin: boolean, application: any) {
|
||||
return !!(
|
||||
(isAdmin && application.buildPack === 'compose') ||
|
||||
((application.fqdn || application.settings.isBot) &&
|
||||
((application.gitSource && application.repository && application.buildPack) ||
|
||||
application.simpleDockerfile) &&
|
||||
application.destinationDocker)
|
||||
);
|
||||
}
|
||||
export const setLocation = (resource: any, settings?: any) => {
|
||||
if (resource.settings.isBot && resource.exposePort) {
|
||||
disabledButton.set(false);
|
||||
return location.set(`http://${dev ? 'localhost' : settings.ipv4}:${resource.exposePort}`);
|
||||
}
|
||||
if (GITPOD_WORKSPACE_URL && resource.exposePort) {
|
||||
const { href } = new URL(GITPOD_WORKSPACE_URL);
|
||||
const newURL = href.replace('https://', `https://${resource.exposePort}-`).replace(/\/$/, '');
|
||||
return location.set(newURL);
|
||||
} else if (CODESANDBOX_HOST) {
|
||||
const newURL = `https://${CODESANDBOX_HOST.replace(/\$PORT/, resource.exposePort)}`;
|
||||
return location.set(newURL);
|
||||
}
|
||||
if (resource.fqdn) {
|
||||
return location.set(resource.fqdn);
|
||||
} else {
|
||||
location.set(null);
|
||||
disabledButton.set(false);
|
||||
}
|
||||
};
|
||||
export const selectedBuildId: any = writable(null)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user