diff --git a/package.json b/package.json index e03c678b0..f0a7b34a7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "coolify", "description": "An open-source & self-hostable Heroku / Netlify alternative.", - "version": "2.0.13", + "version": "2.0.14", "license": "AGPL-3.0", "scripts": { "dev": "docker compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0", @@ -25,9 +25,9 @@ "prepare": "husky install" }, "devDependencies": { - "@sveltejs/adapter-node": "1.0.0-next.67", - "@sveltejs/adapter-static": "1.0.0-next.27", - "@sveltejs/kit": "1.0.0-next.259", + "@sveltejs/adapter-node": "1.0.0-next.68", + "@sveltejs/adapter-static": "1.0.0-next.28", + "@sveltejs/kit": "1.0.0-next.278", "@types/bcrypt": "5.0.0", "@types/js-cookie": "3.0.1", "@types/node": "17.0.18", @@ -50,7 +50,7 @@ "svelte": "3.46.4", "svelte-check": "2.4.3", "svelte-preprocess": "4.10.3", - "tailwindcss": "3.0.22", + "tailwindcss": "3.0.23", "ts-node": "10.5.0", "tslib": "2.3.1", "typescript": "4.5.5" @@ -59,9 +59,9 @@ "dependencies": { "@iarna/toml": "2.2.5", "@prisma/client": "3.9.2", - "@sentry/node": "6.17.8", + "@sentry/node": "6.17.9", "bcrypt": "5.0.1", - "bullmq": "1.72.0", + "bullmq": "1.73.0", "compare-versions": "4.1.3", "cookie": "0.4.2", "cuid": "2.1.8", @@ -69,14 +69,15 @@ "dockerode": "3.3.1", "dotenv-extended": "2.9.0", "generate-password": "1.7.0", - "get-port": "6.0.0", + "get-port": "6.1.0", "got": "12.0.1", "js-cookie": "3.0.1", "js-yaml": "4.1.0", "jsonwebtoken": "8.5.1", "node-forge": "1.2.1", - "svelte-kit-cookie-session": "2.0.2", - "unique-names-generator": "4.6.0" + "svelte-kit-cookie-session": "2.1.2", + "tailwindcss-scrollbar": "^0.1.0", + "unique-names-generator": "4.7.1" }, "prisma": { "seed": "node prisma/seed.cjs" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58ddfd8c6..4b0d07310 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,10 +3,10 @@ lockfileVersion: 5.3 specifiers: '@iarna/toml': 2.2.5 '@prisma/client': 3.9.2 - '@sentry/node': 6.17.8 - '@sveltejs/adapter-node': 1.0.0-next.67 - '@sveltejs/adapter-static': 1.0.0-next.27 - '@sveltejs/kit': 1.0.0-next.259 + '@sentry/node': 6.17.9 + '@sveltejs/adapter-node': 1.0.0-next.68 + '@sveltejs/adapter-static': 1.0.0-next.28 + '@sveltejs/kit': 1.0.0-next.278 '@types/bcrypt': 5.0.0 '@types/js-cookie': 3.0.1 '@types/node': 17.0.18 @@ -16,7 +16,7 @@ specifiers: '@zerodevx/svelte-toast': 0.6.3 autoprefixer: 10.4.2 bcrypt: 5.0.1 - bullmq: 1.72.0 + bullmq: 1.73.0 compare-versions: 4.1.3 cookie: 0.4.2 cross-var: 1.1.0 @@ -28,7 +28,7 @@ specifiers: eslint-config-prettier: 8.3.0 eslint-plugin-svelte3: 3.2.1 generate-password: 1.7.0 - get-port: 6.0.0 + get-port: 6.1.0 got: 12.0.1 husky: 7.0.4 js-cookie: 3.0.1 @@ -43,20 +43,21 @@ specifiers: prisma: 3.9.2 svelte: 3.46.4 svelte-check: 2.4.3 - svelte-kit-cookie-session: 2.0.5 + svelte-kit-cookie-session: 2.1.2 svelte-preprocess: 4.10.3 - tailwindcss: 3.0.22 + tailwindcss: 3.0.23 + tailwindcss-scrollbar: ^0.1.0 ts-node: 10.5.0 tslib: 2.3.1 typescript: 4.5.5 - unique-names-generator: 4.6.0 + unique-names-generator: 4.7.1 dependencies: '@iarna/toml': 2.2.5 '@prisma/client': 3.9.2_prisma@3.9.2 - '@sentry/node': 6.17.8 + '@sentry/node': 6.17.9 bcrypt: 5.0.1 - bullmq: 1.72.0 + bullmq: 1.73.0 compare-versions: 4.1.3 cookie: 0.4.2 cuid: 2.1.8 @@ -64,19 +65,20 @@ dependencies: dockerode: 3.3.1 dotenv-extended: 2.9.0 generate-password: 1.7.0 - get-port: 6.0.0 + get-port: 6.1.0 got: 12.0.1 js-cookie: 3.0.1 js-yaml: 4.1.0 jsonwebtoken: 8.5.1 node-forge: 1.2.1 - svelte-kit-cookie-session: 2.0.5 - unique-names-generator: 4.6.0 + svelte-kit-cookie-session: 2.1.2 + tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.23 + unique-names-generator: 4.7.1 devDependencies: - '@sveltejs/adapter-node': 1.0.0-next.67 - '@sveltejs/adapter-static': 1.0.0-next.27 - '@sveltejs/kit': 1.0.0-next.259_svelte@3.46.4 + '@sveltejs/adapter-node': 1.0.0-next.68 + '@sveltejs/adapter-static': 1.0.0-next.28 + '@sveltejs/kit': 1.0.0-next.278_svelte@3.46.4 '@types/bcrypt': 5.0.0 '@types/js-cookie': 3.0.1 '@types/node': 17.0.18 @@ -99,7 +101,7 @@ devDependencies: svelte: 3.46.4 svelte-check: 2.4.3_postcss@8.4.6+svelte@3.46.4 svelte-preprocess: 4.10.3_88b359da5cac6d8f6ee1bbb7080a3fa9 - tailwindcss: 3.0.22_c940fbabf228b85b1c73d314b43e31f1 + tailwindcss: 3.0.23_c940fbabf228b85b1c73d314b43e31f1 ts-node: 10.5.0_f3bd4037939c2ed2942ba074291f8ef2 tslib: 2.3.1 typescript: 4.5.5 @@ -293,56 +295,56 @@ packages: picomatch: 2.3.0 dev: true - /@sentry/core/6.17.8: + /@sentry/core/6.17.9: resolution: { - integrity: sha512-4WTjgQom75Rvgn6XYy6e7vMIbWlj8utau1wWvr7kjqFKuuuuycRvPgVzAdVr4B3WDHHCInAZpUchsOLs2qwIEA== + integrity: sha512-14KalmTholGUtgdh9TklO+jUpyQ/D3OGkhlH1rnGQGoJgFy2eYm+s+MnUEMxFdGIUCz5kOteuNqYZxaDmFagpQ== } engines: { node: '>=6' } dependencies: - '@sentry/hub': 6.17.8 - '@sentry/minimal': 6.17.8 - '@sentry/types': 6.17.8 - '@sentry/utils': 6.17.8 + '@sentry/hub': 6.17.9 + '@sentry/minimal': 6.17.9 + '@sentry/types': 6.17.9 + '@sentry/utils': 6.17.9 tslib: 1.14.1 dev: false - /@sentry/hub/6.17.8: + /@sentry/hub/6.17.9: resolution: { - integrity: sha512-GW0XYpkoQu/kSJaTLfsF4extHDOBPNRnT0qKr/YO20Z5wGxYp8LsdnAuU3njcFHcAV2F/QDTj2BPq1U385/4+A== + integrity: sha512-34EdrweWDbBV9EzEFIXcO+JeoyQmKzQVJxpTKZoJA6PUwf2NrndaUdjlkDEtBEzjuLUTxhLxtOzEsYs1O6RVcg== } engines: { node: '>=6' } dependencies: - '@sentry/types': 6.17.8 - '@sentry/utils': 6.17.8 + '@sentry/types': 6.17.9 + '@sentry/utils': 6.17.9 tslib: 1.14.1 dev: false - /@sentry/minimal/6.17.8: + /@sentry/minimal/6.17.9: resolution: { - integrity: sha512-VJXFZBO/O8SViK0fdzodxpNr+pbpgczNgLpz/MNuSooV6EBesgCMVjXtxDUp1Ie1odc0GUprN/ZMLYBmYdIrKQ== + integrity: sha512-T3PMCHcKk6lkZq6zKgANrYJJxXBXKOe+ousV1Fas1rVBMv7dtKfsa4itqQHszcW9shusPDiaQKIJ4zRLE5LKmg== } engines: { node: '>=6' } dependencies: - '@sentry/hub': 6.17.8 - '@sentry/types': 6.17.8 + '@sentry/hub': 6.17.9 + '@sentry/types': 6.17.9 tslib: 1.14.1 dev: false - /@sentry/node/6.17.8: + /@sentry/node/6.17.9: resolution: { - integrity: sha512-b3zg1XjKtxp7o821ENORO1CCzMM4QzKP01rzztMwyMcj28dmUq36QXoQAnwdKn7jEYkJdLnMeniIBR6U6NUJrQ== + integrity: sha512-jbn+q7qPGOh6D7nYoYGaAlmuvMDpQmyMwBtUVYybuZp2AALe43O3Z4LtoJ+1+F31XowpsIPZx1mwNs4ZrILskA== } engines: { node: '>=6' } dependencies: - '@sentry/core': 6.17.8 - '@sentry/hub': 6.17.8 - '@sentry/tracing': 6.17.8 - '@sentry/types': 6.17.8 - '@sentry/utils': 6.17.8 + '@sentry/core': 6.17.9 + '@sentry/hub': 6.17.9 + '@sentry/tracing': 6.17.9 + '@sentry/types': 6.17.9 + '@sentry/utils': 6.17.9 cookie: 0.4.2 https-proxy-agent: 5.0.0 lru_map: 0.3.3 @@ -351,36 +353,36 @@ packages: - supports-color dev: false - /@sentry/tracing/6.17.8: + /@sentry/tracing/6.17.9: resolution: { - integrity: sha512-WJ3W8O6iPI3w7MrzTnYcw3s5PGBNFqT4b9oBCl5Ndjexs8DsGlQOxjrsipo36z6TpnRHpAE4FEbOETb2R8JRJQ== + integrity: sha512-5Rb/OS4ryNJLvz2nv6wyjwhifjy6veqaF9ffLrwFYij/WDy7m62ASBblxgeiI3fbPLX0aBRFWIJAq1vko26+AQ== } engines: { node: '>=6' } dependencies: - '@sentry/hub': 6.17.8 - '@sentry/minimal': 6.17.8 - '@sentry/types': 6.17.8 - '@sentry/utils': 6.17.8 + '@sentry/hub': 6.17.9 + '@sentry/minimal': 6.17.9 + '@sentry/types': 6.17.9 + '@sentry/utils': 6.17.9 tslib: 1.14.1 dev: false - /@sentry/types/6.17.8: + /@sentry/types/6.17.9: resolution: { - integrity: sha512-0i0f+dpvV62Pm5QMVBHNfEsTGIXoXRGQbeN2LGL4XbhzrzUmIrBPzrnZHv9c/JYtSJnI6A0B9OG7Bdlh3aku+Q== + integrity: sha512-xuulX6qUCL14ayEOh/h6FUIvZtsi1Bx34dSOaWDrjXUOJHJAM7214uiqW1GZxPJ13YuaUIubjTSfDmSQ9CBzTw== } engines: { node: '>=6' } dev: false - /@sentry/utils/6.17.8: + /@sentry/utils/6.17.9: resolution: { - integrity: sha512-cAOM53A5FHv95hpDuXKJU8rI4B1XdZ6qe3Yo+/nDS9QDpOgzvyjcItgXPvKW1wUjdHCcnwu7VBfBxB7teYOW9g== + integrity: sha512-4eo9Z3JlJCGlGrQRbtZWL+L9NnlUXgTbfK3Lk7oO8D1ev8R5b5+iE6tZHTvU5rQRcq6zu+POT+tK5u9oxc/rnQ== } engines: { node: '>=6' } dependencies: - '@sentry/types': 6.17.8 + '@sentry/types': 6.17.9 tslib: 1.14.1 dev: false @@ -392,28 +394,28 @@ packages: engines: { node: '>=10' } dev: false - /@sveltejs/adapter-node/1.0.0-next.67: + /@sveltejs/adapter-node/1.0.0-next.68: resolution: { - integrity: sha512-+LuLn91xARZsRANiQNIIDpMMncUTnP2pJc8tyL+FdpVvs5UtlvkYJpeCBPFqjjseRpIIbi8Slu89GCdrRXBDUg== + integrity: sha512-MiEjtl15Aupm6bjirVlq0kkc9AL8qDXz/blsh4jYMsaiidmcEHeDgfZQFM5YiXy95DbxV30MAkhwCQiYK/J8Kw== } dependencies: tiny-glob: 0.2.9 dev: true - /@sveltejs/adapter-static/1.0.0-next.27: + /@sveltejs/adapter-static/1.0.0-next.28: resolution: { - integrity: sha512-dcN1p1D7ZY/a9SClfN14mgm9pyWbLxdwM9gzPMZG6xXOoqMtwI03aZOFgGGumHPdv+XcGRZM96vUSRoDm6vBJQ== + integrity: sha512-c4xLyeSwnbGQxe4f1SLpHTbxZDm3TEr43scR3tOlVgQN+mnAL9aDdl3nTtdzWmrUDmDEmY4GriAwLyFLZuINLw== } dependencies: tiny-glob: 0.2.9 dev: true - /@sveltejs/kit/1.0.0-next.259_svelte@3.46.4: + /@sveltejs/kit/1.0.0-next.278_svelte@3.46.4: resolution: { - integrity: sha512-+Tss6cQXmpi4Jno/ZP0zJ3INBLMED+WeW4UI81tmexheC76Y2p+cbInneKO/REx/8QFo1iroYrWAUkZPsOg8Ew== + integrity: sha512-WT93Wnu05X9WG9BMMk/dj0gy6R7iXm9aXRDVgmIl9z8jT2ukejgmkhi5IwBYrK0OMIUALRVfukn+iy+srPc91Q== } engines: { node: '>=14.13' } hasBin: true @@ -1746,10 +1748,10 @@ packages: ieee754: 1.2.1 dev: false - /bullmq/1.72.0: + /bullmq/1.73.0: resolution: { - integrity: sha512-Q0pk6GphHyYsacpjZZFhjp/+TY+2g2FDsJS3qwIyskQL4j7vZaa1iYX3gKDEBn4C5eZMP1EOl9GWkm2bhdB0Wg== + integrity: sha512-+BF7yeGagYD/iMkM3FA8Wvb3j3MyKE/OdXv404+nQjUsKXfL7PbqX5NSA9lBtFzOdyFx9ZWyKRnBwuGQsLfM0w== } dependencies: cron-parser: 2.18.0 @@ -3114,10 +3116,10 @@ packages: engines: { node: '>=8' } dev: false - /get-port/6.0.0: + /get-port/6.1.0: resolution: { - integrity: sha512-qSVkVF6Eq1GdL/cBNiFuP4nUHMF7OEMTqEjC6alR2N90u8BFOoO0PFhNTX2QtAUoGrz8NnrSWj85TZ8YXZ6LOA== + integrity: sha512-JKnPFW/G2ZRirH/25sLK1aLBQktJfQLixzMMuMBP8A2G/ivSaIwdTnlJeO7PWeyhyIGVorezNf6+CXZU9i0cIQ== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } dev: false @@ -5203,10 +5205,10 @@ packages: svelte: 3.46.4 dev: true - /svelte-kit-cookie-session/2.0.5: + /svelte-kit-cookie-session/2.1.2: resolution: { - integrity: sha512-IX1IXtn42UTz/isem1LqH0SAZdCx6Z6Iu2V4Q83V2EScFbXZWfeFY08Azl8ZrPKdIDhSNHBLAAumRjA6TBxCvQ== + integrity: sha512-PfxIWDhiyYWu7iKlL0GHpmwDrdFh+rX/WmBzOuvctF25UqngIo9MCiegWBSBLE1RBwNs5UqaIeI8+vligmY07g== } dev: false @@ -5288,10 +5290,21 @@ packages: strip-ansi: 6.0.1 dev: true - /tailwindcss/3.0.22_c940fbabf228b85b1c73d314b43e31f1: + /tailwindcss-scrollbar/0.1.0_tailwindcss@3.0.23: resolution: { - integrity: sha512-F8lt74RlNZirnkaSk310+vGQta7c0/hgx7/bqxruM4wS9lp8oqV93lzavajC3VT0Lp4UUtUVIt8ifKcmGzkr0A== + integrity: sha512-egipxw4ooQDh94x02XQpPck0P0sfwazwoUGfA9SedPATIuYDR+6qe8d31Gl7YsSMRiOKDkkqfI0kBvEw9lT/Hg== + } + peerDependencies: + tailwindcss: '>= 2.x.x' + dependencies: + tailwindcss: 3.0.23_c940fbabf228b85b1c73d314b43e31f1 + dev: false + + /tailwindcss/3.0.23_c940fbabf228b85b1c73d314b43e31f1: + resolution: + { + integrity: sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA== } engines: { node: '>=12.13.0' } hasBin: true @@ -5515,10 +5528,10 @@ packages: function.name: 1.0.13 dev: false - /unique-names-generator/4.6.0: + /unique-names-generator/4.7.1: resolution: { - integrity: sha512-m0fke1emBeT96UYn2psPQYwljooDWRTKt9oUZ5vlt88ZFMBGxqwPyLHXwCfkbgdm8jzioCp7oIpo6KdM+fnUlQ== + integrity: sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow== } engines: { node: '>=8' } dev: false diff --git a/prisma/migrations/20220217211304_dualcerts/migration.sql b/prisma/migrations/20220217211304_dualcerts/migration.sql new file mode 100644 index 000000000..a6ea0a57d --- /dev/null +++ b/prisma/migrations/20220217211304_dualcerts/migration.sql @@ -0,0 +1,47 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Setting" ("createdAt", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +CREATE TABLE "new_ApplicationSettings" ( + "id" TEXT NOT NULL PRIMARY KEY, + "applicationId" TEXT NOT NULL, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "debug" BOOLEAN NOT NULL DEFAULT false, + "previews" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_ApplicationSettings" ("applicationId", "createdAt", "debug", "id", "previews", "updatedAt") SELECT "applicationId", "createdAt", "debug", "id", "previews", "updatedAt" FROM "ApplicationSettings"; +DROP TABLE "ApplicationSettings"; +ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; +CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); +CREATE TABLE "new_Service" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "fqdn" TEXT, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "type" TEXT, + "version" TEXT, + "destinationDockerId" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Service_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_Service" ("createdAt", "destinationDockerId", "fqdn", "id", "name", "type", "updatedAt", "version") SELECT "createdAt", "destinationDockerId", "fqdn", "id", "name", "type", "updatedAt", "version" FROM "Service"; +DROP TABLE "Service"; +ALTER TABLE "new_Service" RENAME TO "Service"; +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b064d09ab..cf9684f53 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -11,6 +11,7 @@ model Setting { id String @id @default(cuid()) fqdn String? @unique isRegistrationEnabled Boolean @default(false) + dualCerts Boolean @default(false) proxyPassword String proxyUser String createdAt DateTime @default(now()) @@ -97,6 +98,7 @@ model ApplicationSettings { id String @id @default(cuid()) application Application @relation(fields: [applicationId], references: [id]) applicationId String @unique + dualCerts Boolean @default(false) debug Boolean @default(false) previews Boolean @default(false) createdAt DateTime @default(now()) @@ -105,7 +107,7 @@ model ApplicationSettings { model Secret { id String @id @default(cuid()) - name String + name String value String isBuildSecret Boolean @default(false) createdAt DateTime @default(now()) @@ -234,6 +236,7 @@ model Service { id String @id @default(cuid()) name String fqdn String? + dualCerts Boolean @default(false) type String? version String? teams Team[] diff --git a/src/global.d.ts b/src/app.d.ts similarity index 50% rename from src/global.d.ts rename to src/app.d.ts index 4a2702fbc..c3921e0f2 100644 --- a/src/global.d.ts +++ b/src/app.d.ts @@ -1,74 +1,26 @@ /// -interface Cookies { - teamId?: string; - gitlabToken?: string; - 'kit.session'?: string; -} -interface Locals { - gitlabToken?: string; - user: { - teamId: string; - permission: string; - isAdmin: boolean; - }; - session: { - data: { - uid?: string; - teams?: string[]; - expires?: string; - }; - }; + +declare namespace App { + interface Locals { + session: import('svelte-kit-cookie-session').Session; + cookies: Record; + } + interface Platform {} + interface Session extends SessionData {} + interface Stuff {} } -type Applications = { - name: string; - domain: string; -}; - -interface Hash { - iv: string; - content: string; +interface SessionData { + version?: string; + userId?: string | null; + teamId?: string | null; + permission?: string; + isAdmin?: boolean; + expires?: string | null; + gitlabToken?: string | null; + ghToken?: string | null; } -interface BuildPack { - name: string; -} - -// TODO: Not used, not working what?! -enum GitSource { - Github = 'github', - Gitlab = 'gitlab', - Bitbucket = 'bitbucket' -} - -type RawHaproxyConfiguration = { - _version: number; - data: string; -}; - -type NewTransaction = { - _version: number; - id: string; - status: string; -}; - -type HttpRequestRuleForceSSL = { - return_hdrs: null; - cond: string; - cond_test: string; - index: number; - redir_code: number; - redir_type: string; - redir_value: string; - type: string; -}; - -// TODO: No any please -type HttpRequestRule = { - _version: number; - data: Array; -}; - type DateTimeFormatOptions = { localeMatcher?: 'lookup' | 'best fit'; weekday?: 'long' | 'short' | 'narrow'; @@ -84,3 +36,24 @@ type DateTimeFormatOptions = { hour12?: boolean; timeZone?: string; }; + +interface Hash { + iv: string; + content: string; +} + +type RawHaproxyConfiguration = { + _version: number; + data: string; +}; + +type NewTransaction = { + _version: number; + id: string; + status: string; +}; + +type Application = { + name: string; + domain: string; +}; diff --git a/src/hooks.ts b/src/hooks.ts index d4fea1baf..7bb2d8060 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -2,7 +2,7 @@ import dotEnvExtended from 'dotenv-extended'; dotEnvExtended.load(); import type { GetSession } from '@sveltejs/kit'; import { handleSession } from 'svelte-kit-cookie-session'; -import { getUserDetails, isTeamIdTokenAvailable, sentry } from '$lib/common'; +import { getUserDetails, sentry } from '$lib/common'; import { version } from '$lib/common'; import cookie from 'cookie'; import { dev } from '$app/env'; @@ -10,27 +10,38 @@ import { dev } from '$app/env'; export const handle = handleSession( { secret: process.env['COOLIFY_SECRET_KEY'], - expires: 30 + expires: 30, + cookie: { secure: false } }, async function ({ event, resolve }) { let response; try { - const cookies: Cookies = cookie.parse(event.request.headers.get('cookie') || ''); - if (cookies['kit.session']) { - const { permission, teamId } = await getUserDetails(event, false); - event.locals.user = { - teamId, - permission, - isAdmin: permission === 'admin' || permission === 'owner' - }; - } - if (cookies.gitlabToken) { - event.locals.gitlabToken = cookies.gitlabToken; + if (event.locals.cookies) { + let gitlabToken = event.locals.cookies.gitlabToken || null; + let ghToken = event.locals.cookies.ghToken; + if (event.locals.cookies['kit.session']) { + const { permission, teamId, userId } = await getUserDetails(event, false); + const newSession = { + userId, + teamId, + permission, + isAdmin: permission === 'admin' || permission === 'owner', + expires: event.locals.session.data.expires, + gitlabToken, + ghToken + }; + + if (JSON.stringify(event.locals.session.data) !== JSON.stringify(newSession)) { + event.locals.session.data = { ...newSession }; + } + } } + response = await resolve(event, { ssr: !event.url.pathname.startsWith('/webhooks/success') }); } catch (error) { + console.log(error); response = await resolve(event, { ssr: !event.url.pathname.startsWith('/webhooks/success') }); @@ -61,17 +72,13 @@ export const handle = handleSession( } ); -export const getSession: GetSession = function (request) { +export const getSession: GetSession = function ({ locals }) { return { version, - gitlabToken: request.locals?.gitlabToken || null, - uid: request.locals.session.data?.uid || null, - teamId: request.locals.user?.teamId || null, - permission: request.locals.user?.permission, - isAdmin: request.locals.user?.isAdmin || false + ...locals.session.data }; }; export async function handleError({ error, event }) { - if (!dev) sentry.captureException(error, { event }); + if (!dev) sentry.captureException(error, event); } diff --git a/src/lib/common.ts b/src/lib/common.ts index 3740ba231..3ec0c923c 100644 --- a/src/lib/common.ts +++ b/src/lib/common.ts @@ -67,7 +67,7 @@ export const isTeamIdTokenAvailable = (request) => { }; export const getTeam = (event) => { - const cookies: Cookies = Cookie.parse(event.request.headers.get('cookie')); + const cookies = Cookie.parse(event.request.headers.get('cookie')); if (cookies.teamId) { return cookies.teamId; } else if (event.locals.session.data.teamId) { @@ -78,7 +78,7 @@ export const getTeam = (event) => { export const getUserDetails = async (event, isAdminRequired = true) => { const teamId = getTeam(event); - const userId = event.locals.session.data.uid || null; + const userId = event.locals.session.data.userId || null; const { permission = 'read' } = await db.prisma.permission.findFirst({ where: { teamId, userId }, select: { permission: true }, diff --git a/src/lib/components/CopyPasswordField.svelte b/src/lib/components/CopyPasswordField.svelte index ab43f48bd..ac2ba94d4 100644 --- a/src/lib/components/CopyPasswordField.svelte +++ b/src/lib/components/CopyPasswordField.svelte @@ -29,7 +29,7 @@ } - showActions(true)} on:mouseleave={() => showActions(false)} @@ -78,7 +78,7 @@ {/if} {#if actionsShow} -
+
{#if isPasswordField}
(showPassword = !showPassword)}> @@ -142,4 +142,4 @@
{/if} - +
diff --git a/src/lib/components/Explainer.svelte b/src/lib/components/Explainer.svelte index 4eaf3a398..b47c03ec1 100644 --- a/src/lib/components/Explainer.svelte +++ b/src/lib/components/Explainer.svelte @@ -1,6 +1,6 @@ -
{@html text}
+
{@html text}
diff --git a/src/lib/components/Loading.svelte b/src/lib/components/Loading.svelte index de113e261..99a1054ea 100644 --- a/src/lib/components/Loading.svelte +++ b/src/lib/components/Loading.svelte @@ -11,7 +11,7 @@
{:else} -
+
{/if} diff --git a/src/lib/components/Setting.svelte b/src/lib/components/Setting.svelte index b26e3e072..680203c95 100644 --- a/src/lib/components/Setting.svelte +++ b/src/lib/components/Setting.svelte @@ -4,15 +4,18 @@ export let setting; export let title; export let description; - export let isPadding = true; + export let isCenter = true; export let disabled = false; + export let dataTooltip = null; -
  • -
    -

    {title}

    +
    +
    +
    {title}
    +
    +
    - -
  • +
    diff --git a/src/lib/database/applications.ts b/src/lib/database/applications.ts index 738ebe594..ffcafbecd 100644 --- a/src/lib/database/applications.ts +++ b/src/lib/database/applications.ts @@ -1,5 +1,5 @@ import { decrypt, encrypt } from '$lib/crypto'; -import { removeProxyConfiguration, removeWwwRedirection } from '$lib/haproxy'; +import { removeProxyConfiguration } from '$lib/haproxy'; import { asyncExecShell, getEngine } from '$lib/common'; import { getDomain, removeDestinationDocker } from '$lib/common'; @@ -209,10 +209,10 @@ export async function configureApplication({ }); } -export async function setApplicationSettings({ id, debug, previews }) { +export async function setApplicationSettings({ id, debug, previews, dualCerts }) { return await prisma.application.update({ where: { id }, - data: { settings: { update: { debug, previews } } }, + data: { settings: { update: { debug, previews, dualCerts } } }, include: { destinationDocker: true } }); } diff --git a/src/lib/database/checks.ts b/src/lib/database/checks.ts index 7cc066605..35af8dc00 100644 --- a/src/lib/database/checks.ts +++ b/src/lib/database/checks.ts @@ -21,16 +21,35 @@ export async function isSecretExists({ id, name }) { export async function isDomainConfigured({ id, fqdn }) { const domain = getDomain(fqdn); + const nakedDomain = domain.replace('www.', ''); const foundApp = await prisma.application.findFirst({ - where: { fqdn: { endsWith: `//${domain}` }, id: { not: id } }, + where: { + OR: [ + { fqdn: { endsWith: `//${nakedDomain}` } }, + { fqdn: { endsWith: `//www.${nakedDomain}` } } + ], + id: { not: id } + }, select: { fqdn: true } }); const foundService = await prisma.service.findFirst({ - where: { fqdn: { endsWith: `//${domain}` }, id: { not: id } }, + where: { + OR: [ + { fqdn: { endsWith: `//${nakedDomain}` } }, + { fqdn: { endsWith: `//www.${nakedDomain}` } } + ], + id: { not: id } + }, select: { fqdn: true } }); const coolifyFqdn = await prisma.setting.findFirst({ - where: { fqdn: { endsWith: `//${domain}` }, id: { not: id } }, + where: { + OR: [ + { fqdn: { endsWith: `//${nakedDomain}` } }, + { fqdn: { endsWith: `//www.${nakedDomain}` } } + ], + id: { not: id } + }, select: { fqdn: true } }); if (foundApp || foundService || coolifyFqdn) return true; diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts index 290ecd2b5..04bf54d6b 100644 --- a/src/lib/database/common.ts +++ b/src/lib/database/common.ts @@ -2,6 +2,7 @@ import { dev } from '$app/env'; import { sentry } from '$lib/common'; import * as Prisma from '@prisma/client'; import { default as ProdPrisma } from '@prisma/client'; +import type { PrismaClientOptions } from '@prisma/client/runtime'; import generator from 'generate-password'; import forge from 'node-forge'; @@ -19,28 +20,20 @@ if (!dev) { PrismaClient = ProdPrisma.PrismaClient; P = ProdPrisma.Prisma; } -let prismaOptions = { + +export const prisma = new PrismaClient({ + errorFormat: 'pretty', rejectOnNotFound: false -}; -if (dev) { - prismaOptions = { - errorFormat: 'pretty', - rejectOnNotFound: false, - log: [ - { - emit: 'event', - level: 'query' - } - ] - }; -} -export const prisma = new PrismaClient(prismaOptions); +}); export function ErrorHandler(e) { if (e! instanceof Error) { e = new Error(e.toString()); } let truncatedError = e; + if (e.stdout) { + truncatedError = e.stdout; + } if (e.message?.includes('docker run')) { let truncatedArray = []; truncatedArray = truncatedError.message.split('-').filter((line) => { diff --git a/src/lib/database/services.ts b/src/lib/database/services.ts index 5568cd4a7..5cbf2cbfb 100644 --- a/src/lib/database/services.ts +++ b/src/lib/database/services.ts @@ -107,13 +107,20 @@ export async function configureServiceType({ id, type }) { }); } } -export async function setService({ id, version }) { +export async function setServiceVersion({ id, version }) { return await prisma.service.update({ where: { id }, data: { version } }); } +export async function setServiceSettings({ id, dualCerts }) { + return await prisma.service.update({ + where: { id }, + data: { dualCerts } + }); +} + export async function updatePlausibleAnalyticsService({ id, fqdn, email, username, name }) { await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } }); await prisma.service.update({ where: { id }, data: { name, fqdn } }); diff --git a/src/lib/database/users.ts b/src/lib/database/users.ts index 5c99143a9..9c09d0aaf 100644 --- a/src/lib/database/users.ts +++ b/src/lib/database/users.ts @@ -6,19 +6,23 @@ import { asyncExecShell, uniqueName } from '$lib/common'; import * as db from '$lib/database'; import { startCoolifyProxy } from '$lib/haproxy'; - -export async function login({ email, password }) { +export async function hashPassword(password: string) { const saltRounds = 15; + return bcrypt.hash(password, saltRounds); +} +export async function login({ email, password }) { const users = await prisma.user.count(); const userFound = await prisma.user.findUnique({ where: { email }, - include: { teams: true }, + include: { teams: true, permission: true }, rejectOnNotFound: false }); // Registration disabled if database is not seeded properly const { isRegistrationEnabled, id } = await db.listSettings(); let uid = cuid(); + let permission = 'read'; + let isAdmin = false; // Disable registration if we are registering the first user. if (users === 0) { await prisma.setting.update({ where: { id }, data: { isRegistrationEnabled: false } }); @@ -50,6 +54,8 @@ export async function login({ email, password }) { }; } uid = userFound.id; + // permission = userFound.permission; + isAdmin = true; } } else { // If registration disabled, return 403 @@ -59,8 +65,10 @@ export async function login({ email, password }) { }; } - const hashedPassword = await bcrypt.hash(password, saltRounds); + const hashedPassword = await hashPassword(password); if (users === 0) { + permission = 'owner'; + isAdmin = true; await prisma.user.create({ data: { id: uid, @@ -103,8 +111,10 @@ export async function login({ email, password }) { 'Set-Cookie': `teamId=${uid}; HttpOnly; Path=/; Max-Age=15778800;` }, body: { - uid, - teamId: uid + userId: uid, + teamId: uid, + permission, + isAdmin } }; } diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index 0eaee0109..4e14be487 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -2,7 +2,6 @@ import { dev } from '$app/env'; import { asyncExecShell, getDomain, getEngine } from '$lib/common'; import got from 'got'; import * as db from '$lib/database'; -import { letsEncrypt } from '$lib/letsencrypt'; const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555'; @@ -49,7 +48,8 @@ export async function completeTransaction(transactionId) { return await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); } -export async function removeProxyConfiguration({ domain }) { +export async function removeProxyConfiguration(fqdn) { + const domain = getDomain(fqdn); const haproxy = await haproxyInstance(); const backendFound = await haproxy .get(`v2/services/haproxy/configuration/backends/${domain}`) @@ -65,97 +65,92 @@ export async function removeProxyConfiguration({ domain }) { .json(); await completeTransaction(transactionId); } - await removeWwwRedirection(domain); + await forceSSLOffApplication(domain); + await removeWwwRedirection(fqdn); } -export async function forceSSLOffApplication({ domain }) { - if (!dev) { - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - let transactionId; - try { - const rules: any = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => rule.cond_test.includes(`-i ${domain}`)); - if (rule) { - transactionId = await getNextTransactionId(); - - await haproxy - .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); +export async function forceSSLOffApplication(domain) { + const haproxy = await haproxyInstance(); + await checkHAProxy(haproxy); + let transactionId; + try { + const rules: any = await haproxy + .get(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + parent_name: 'http', + parent_type: 'frontend' } + }) + .json(); + if (rules.data.length > 0) { + const rule = rules.data.find((rule) => + rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) + ); + if (rule) { + transactionId = await getNextTransactionId(); + + await haproxy + .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { + searchParams: { + transaction_id: transactionId, + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); } - } catch (error) { - console.log(error); - } finally { - if (transactionId) await completeTransaction(transactionId); } - } else { - console.log(`[DEBUG] Removing ssl for ${domain}`); + } catch (error) { + console.log(error); + } finally { + if (transactionId) await completeTransaction(transactionId); } } -export async function forceSSLOnApplication({ domain }) { - if (!dev) { - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - let transactionId; - try { - const rules: any = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - let nextRule = 0; - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => - rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) - ); - if (rule) return; - nextRule = rules.data[rules.data.length - 1].index + 1; - } - transactionId = await getNextTransactionId(); - - await haproxy - .post(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - }, - json: { - index: nextRule, - cond: 'if', - cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`, - type: 'redirect', - redir_type: 'scheme', - redir_value: 'https', - redir_code: 301 - } - }) - .json(); - } catch (error) { - console.log(error); - throw error; - } finally { - if (transactionId) await completeTransaction(transactionId); +export async function forceSSLOnApplication(domain) { + const haproxy = await haproxyInstance(); + await checkHAProxy(haproxy); + let transactionId; + try { + const rules: any = await haproxy + .get(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); + let nextRule = 0; + if (rules.data.length > 0) { + const rule = rules.data.find((rule) => + rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) + ); + if (rule) return; + nextRule = rules.data[rules.data.length - 1].index + 1; } - } else { - console.log(`[DEBUG] Adding ssl for ${domain}`); + transactionId = await getNextTransactionId(); + + await haproxy + .post(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + transaction_id: transactionId, + parent_name: 'http', + parent_type: 'frontend' + }, + json: { + index: nextRule, + cond: 'if', + cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`, + type: 'redirect', + redir_type: 'scheme', + redir_value: 'https', + redir_code: dev ? 302 : 301 + } + }) + .json(); + } catch (error) { + console.log(error); + throw error; + } finally { + if (transactionId) await completeTransaction(transactionId); } } @@ -274,6 +269,7 @@ export async function configureProxyForApplication({ domain, imageId, applicatio export async function configureCoolifyProxyOff(fqdn) { const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); @@ -288,10 +284,8 @@ export async function configureCoolifyProxyOff(fqdn) { }) .json(); await completeTransaction(transactionId); - if (!dev) { - await forceSSLOffApplication({ domain }); - } - await setWwwRedirection(fqdn); + if (isHttps) await forceSSLOffApplication(domain); + await removeWwwRedirection(fqdn); } catch (error) { throw error?.response?.body || error; } @@ -565,7 +559,8 @@ export async function configureSimpleServiceProxyOn({ id, domain, port }) { await completeTransaction(transactionId); } -export async function configureSimpleServiceProxyOff({ domain }) { +export async function configureSimpleServiceProxyOff(fqdn) { + const domain = getDomain(fqdn); const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); try { @@ -580,11 +575,16 @@ export async function configureSimpleServiceProxyOff({ domain }) { .json(); await completeTransaction(transactionId); } catch (error) {} - await removeWwwRedirection(domain); + await forceSSLOffApplication(domain); + await removeWwwRedirection(fqdn); return; } -export async function removeWwwRedirection(domain) { +export async function removeWwwRedirection(fqdn) { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; + const haproxy = await haproxyInstance(); await checkHAProxy(); const rules: any = await haproxy @@ -596,9 +596,7 @@ export async function removeWwwRedirection(domain) { }) .json(); if (rules.data.length > 0) { - const rule = rules.data.find((rule) => - rule.redir_value.includes(`${domain}%[capture.req.uri]`) - ); + const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); if (rule) { const transactionId = await getNextTransactionId(); await haproxy @@ -623,6 +621,7 @@ export async function setWwwRedirection(fqdn) { const domain = getDomain(fqdn); const isHttps = fqdn.startsWith('https://'); const isWWW = fqdn.includes('www.'); + const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; const contTest = `{ req.hdr(host) -i ${isWWW ? domain.replace('www.', '') : `www.${domain}`} }`; const rules: any = await haproxy .get(`v2/services/haproxy/configuration/http_request_rules`, { @@ -634,13 +633,11 @@ export async function setWwwRedirection(fqdn) { .json(); let nextRule = 0; if (rules.data.length > 0) { - const rule = rules.data.find((rule) => - rule.redir_value.includes(`${domain}%[capture.req.uri]`) - ); + const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); if (rule) return; nextRule = rules.data[rules.data.length - 1].index + 1; } - const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; + transactionId = await getNextTransactionId(); await haproxy .post(`v2/services/haproxy/configuration/http_request_rules`, { diff --git a/src/lib/letsencrypt.ts b/src/lib/letsencrypt.ts index 86d9e6e25..dc4c9d6f7 100644 --- a/src/lib/letsencrypt.ts +++ b/src/lib/letsencrypt.ts @@ -1,50 +1,78 @@ import { dev } from '$app/env'; -import { forceSSLOffApplication, forceSSLOnApplication, getNextTransactionId } from '$lib/haproxy'; +import { forceSSLOffApplication, forceSSLOnApplication } from '$lib/haproxy'; import { asyncExecShell, getEngine } from './common'; import * as db from '$lib/database'; import cuid from 'cuid'; +import getPort from 'get-port'; export async function letsEncrypt({ domain, isCoolify = false, id = null }) { try { + const nakedDomain = domain.replace('www.', ''); + const wwwDomain = `www.${nakedDomain}`; const randomCuid = cuid(); - if (dev) { - return await forceSSLOnApplication({ domain }); - } else { - if (isCoolify) { - await asyncExecShell( - `docker run --rm --name certbot-${randomCuid} -p 9080:9080 -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port 9080 -d ${domain} --agree-tos --non-interactive --register-unsafely-without-email` - ); + const randomPort = 9080; - const { stderr } = await asyncExecShell( - `docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem` - ); - if (stderr) throw new Error(stderr); - return; + let host; + let dualCerts = false; + if (isCoolify) { + const data = await db.prisma.setting.findFirst(); + dualCerts = data.dualCerts; + host = 'unix:///var/run/docker.sock'; + } else { + // Check Application + const applicationData = await db.prisma.application.findUnique({ + where: { id }, + include: { destinationDocker: true, settings: true } + }); + if (applicationData) { + if (applicationData?.destinationDockerId && applicationData?.destinationDocker) { + host = getEngine(applicationData.destinationDocker.engine); + } + if (applicationData?.settings?.dualCerts) { + dualCerts = applicationData.settings.dualCerts; + } } - let data: any = await db.prisma.application.findUnique({ + // Check Service + const serviceData = await db.prisma.service.findUnique({ where: { id }, include: { destinationDocker: true } }); - if (!data) { - data = await db.prisma.service.findUnique({ - where: { id }, - include: { destinationDocker: true } - }); - } - // Set SSL with Let's encrypt - if (data.destinationDockerId && data.destinationDocker) { - const host = getEngine(data.destinationDocker.engine); - await asyncExecShell( - `DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:9080 -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port 9080 -d ${domain} --agree-tos --non-interactive --register-unsafely-without-email` - ); - const { stderr } = await asyncExecShell( - `DOCKER_HOST=${host} docker run --rm --name bash-${randomCuid} -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem` - ); - if (stderr) throw new Error(stderr); - await forceSSLOnApplication({ domain }); + if (serviceData) { + if (serviceData?.destinationDockerId && serviceData?.destinationDocker) { + host = getEngine(serviceData.destinationDocker.engine); + } + if (serviceData?.dualCerts) { + dualCerts = serviceData.dualCerts; + } } } + await forceSSLOffApplication(domain); + if (dualCerts) { + await asyncExecShell( + `DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${randomPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${randomPort} -d ${nakedDomain} -d ${wwwDomain} --expand --agree-tos --non-interactive --register-unsafely-without-email ${ + dev ? '--test-cert' : '' + }` + ); + await asyncExecShell( + `DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "test -d /etc/letsencrypt/live/${nakedDomain}/ && cat /etc/letsencrypt/live/${nakedDomain}/fullchain.pem /etc/letsencrypt/live/${nakedDomain}/privkey.pem > /app/ssl/${nakedDomain}.pem || cat /etc/letsencrypt/live/${wwwDomain}/fullchain.pem /etc/letsencrypt/live/${wwwDomain}/privkey.pem > /app/ssl/${wwwDomain}.pem"` + ); + } else { + await asyncExecShell( + `DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${randomPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${randomPort} -d ${domain} --expand --agree-tos --non-interactive --register-unsafely-without-email ${ + dev ? '--test-cert' : '' + }` + ); + await asyncExecShell( + `DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem"` + ); + } } catch (error) { - throw error; + if (error.code !== 0) { + throw error; + } + } finally { + if (!isCoolify) { + await forceSSLOnApplication(domain); + } } } diff --git a/src/lib/queues/builder.ts b/src/lib/queues/builder.ts index 41b11dc1e..2e7fe2475 100644 --- a/src/lib/queues/builder.ts +++ b/src/lib/queues/builder.ts @@ -239,6 +239,8 @@ export default async function (job) { if (stderr) console.log(stderr); saveBuildLog({ line: 'Deployment successful!', buildId, applicationId }); } catch (error) { + saveBuildLog({ line: error, buildId, applicationId }); + sentry.captureException(error); throw new Error(error); } try { @@ -257,7 +259,9 @@ export default async function (job) { }); } } catch (error) { + saveBuildLog({ line: error.stdout || error, buildId, applicationId }); sentry.captureException(error); + throw new Error(error); } } } diff --git a/src/lib/queues/index.ts b/src/lib/queues/index.ts index f8525c4e0..3d0dda4f1 100644 --- a/src/lib/queues/index.ts +++ b/src/lib/queues/index.ts @@ -127,7 +127,6 @@ buildWorker.on('completed', async (job: Bullmq.Job) => { }); buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => { - console.log(failedReason); try { await prisma.build.update({ where: { id: job.data.build_id }, data: { status: 'failed' } }); } catch (error) { @@ -136,7 +135,11 @@ buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => { const workdir = `/tmp/build-sources/${job.data.repository}`; await asyncExecShell(`rm -fr ${workdir}`); } - saveBuildLog({ line: 'Failed build!', buildId: job.data.build_id, applicationId: job.data.id }); + saveBuildLog({ + line: 'Failed to deploy!', + buildId: job.data.build_id, + applicationId: job.data.id + }); saveBuildLog({ line: `Reason: ${failedReason.toString()}`, buildId: job.data.build_id, diff --git a/src/lib/queues/proxy.ts b/src/lib/queues/proxy.ts index 92d14f738..36ac37ff9 100644 --- a/src/lib/queues/proxy.ts +++ b/src/lib/queues/proxy.ts @@ -48,7 +48,7 @@ export default async function () { port }); const isHttps = fqdn.startsWith('https://'); - if (isHttps) await forceSSLOnApplication({ domain }); + if (isHttps) await forceSSLOnApplication(domain); await setWwwRedirection(fqdn); } } @@ -98,7 +98,7 @@ export default async function () { await configureCoolifyProxyOn(fqdn); await setWwwRedirection(fqdn); const isHttps = fqdn.startsWith('https://'); - if (isHttps) await forceSSLOnApplication({ domain }); + if (isHttps) await forceSSLOnApplication(domain); } } catch (error) { console.log(error); diff --git a/src/lib/settings.ts b/src/lib/settings.ts index d9e7cff2c..79742e434 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -1,5 +1,7 @@ export const publicPaths = [ '/login', + '/reset', + '/reset/password', '/webhooks/success', '/webhooks/github', '/webhooks/github/install', diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index 027a11e3d..04310b8b3 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -2,14 +2,14 @@ import type { Load } from '@sveltejs/kit'; import { publicPaths } from '$lib/settings'; - export const load: Load = async ({ fetch, url, params, session }) => { - if (!session.uid && !publicPaths.includes(url.pathname)) { + export const load: Load = async ({ fetch, url, session }) => { + if (!session.userId && !publicPaths.includes(url.pathname)) { return { status: 302, redirect: '/login' }; } - if (!session.uid) { + if (!session.userId) { return {}; } const endpoint = `/teams.json`; @@ -49,7 +49,7 @@ }; let latestVersion = 'latest'; onMount(async () => { - if ($session.uid) { + if ($session.userId) { const overrideVersion = browser && window.localStorage.getItem('latestVersion'); try { await get(`/login.json`); @@ -84,7 +84,7 @@ } async function switchTeam() { try { - await post(`/index.json?from=${$page.url.pathname}`, { + await post(`/dashboard.json?from=${$page.url.pathname}`, { cookie: 'teamId', value: selectedTeamId }); @@ -129,7 +129,7 @@ Coolify -{#if $session.uid} +{#if $session.userId}