diff --git a/.env.template b/.env.template index ce02dc5bd..0fa7427f3 100644 --- a/.env.template +++ b/.env.template @@ -2,4 +2,5 @@ COOLIFY_APP_ID= COOLIFY_SECRET_KEY=12341234123412341234123412341234 COOLIFY_DATABASE_URL=file:../db/dev.db COOLIFY_SENTRY_DSN= -COOLIFY_IS_ON="docker" \ No newline at end of file +COOLIFY_IS_ON="docker" +COOLIFY_WHITE_LABELED="false" \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index dc0378c34..fab6428a1 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -yarn lint-staged \ No newline at end of file +pnpm lint-staged diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff57a6abd..8a21ec10a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ First of all, thank you for considering to contribute to my project! It means a - Push to your fork repo - Create a pull request: https://github.com/coollabsio/compare - Write a proper description -- Click "Change to draft" +- Open the pull request to review # How to start after you set up your local fork? @@ -29,7 +29,8 @@ You need to have [Docker Engine](https://docs.docker.com/engine/install/) instal - Copy `.env.template` to `.env` and set the `COOLIFY_APP_ID` environment variable to something cool. - Install dependencies with `pnpm install`. - Need to create a local SQlite database with `pnpm db:push`. - - This will apply all migrations and seed the database at `db/dev.db`. + - This will apply all migrations at `db/dev.db`. +- Seed the database with base entities with `pnpm db:seed` - You can start coding after starting `pnpm dev`. ## Database migrations diff --git a/Dockerfile b/Dockerfile index 4b7b67b85..fa19947eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ COPY --from=coollabsio/prisma-engine:latest /prisma-engines/query-engine /prisma COPY --from=install /app/node_modules ./node_modules COPY . . -RUN apk add --no-cache git openssh-client curl jq sqlite +RUN apk add --no-cache git git-lfs openssh-client curl jq cmake sqlite openssl RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@6 RUN pnpm add -g pnpm diff --git a/README.md b/README.md index 7d02bca45..2cb2d5bf6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ An open-source & self-hostable Heroku / Netlify alternative. https://demo.coolify.io/ -(If it is unresponsible, that means someone overloaded the server. 🙃) +(If it is unresponsive, that means someone overloaded the server. 🙃) ## How to install diff --git a/package.json b/package.json index 922ffc220..d19791e10 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "coolify", "description": "An open-source & self-hostable Heroku / Netlify alternative.", - "version": "2.2.7", + "version": "2.4.2", "license": "AGPL-3.0", "scripts": { - "dev": "docker-compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev", + "dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev", "dev:stop": "docker-compose -f docker-compose-dev.yaml down", "dev:logs": "docker-compose -f docker-compose-dev.yaml logs -f --tail 10", "studio": "npx prisma studio", @@ -29,13 +29,14 @@ "@sveltejs/adapter-node": "1.0.0-next.73", "@sveltejs/kit": "1.0.0-next.303", "@types/js-cookie": "3.0.1", - "@types/js-yaml": "^4.0.5", + "@types/js-yaml": "4.0.5", "@types/node": "17.0.23", "@types/node-forge": "1.0.1", "@typescript-eslint/eslint-plugin": "4.31.1", "@typescript-eslint/parser": "4.31.1", "@zerodevx/svelte-toast": "0.7.1", "autoprefixer": "10.4.4", + "cross-env": "7.0.3", "cross-var": "1.1.0", "eslint": "7.32.0", "eslint-config-prettier": "8.5.0", @@ -50,7 +51,7 @@ "svelte": "3.46.4", "svelte-check": "2.4.6", "svelte-preprocess": "4.10.4", - "svelte-select": "^4.4.7", + "svelte-select": "4.4.7", "tailwindcss": "3.0.23", "ts-node": "10.7.0", "tslib": "2.3.1", @@ -65,7 +66,7 @@ "bullmq": "1.78.1", "compare-versions": "4.1.3", "cookie": "0.4.2", - "cooltipz-css": "^2.1.0", + "cooltipz-css": "2.1.0", "cuid": "2.1.8", "dayjs": "1.11.0", "dockerode": "3.3.1", @@ -76,10 +77,11 @@ "js-cookie": "3.0.1", "js-yaml": "4.1.0", "jsonwebtoken": "8.5.1", - "mustache": "^4.2.0", + "mustache": "4.2.0", "node-forge": "1.3.0", + "p-limit": "4.0.0", "svelte-kit-cookie-session": "2.1.2", - "tailwindcss-scrollbar": "^0.1.0", + "tailwindcss-scrollbar": "0.1.0", "unique-names-generator": "4.7.1" }, "prisma": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29d97a688..fca22ec75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,7 @@ specifiers: '@sveltejs/adapter-node': 1.0.0-next.73 '@sveltejs/kit': 1.0.0-next.303 '@types/js-cookie': 3.0.1 - '@types/js-yaml': ^4.0.5 + '@types/js-yaml': 4.0.5 '@types/node': 17.0.23 '@types/node-forge': 1.0.1 '@typescript-eslint/eslint-plugin': 4.31.1 @@ -18,7 +18,8 @@ specifiers: bullmq: 1.78.1 compare-versions: 4.1.3 cookie: 0.4.2 - cooltipz-css: ^2.1.0 + cooltipz-css: 2.1.0 + cross-env: 7.0.3 cross-var: 1.1.0 cuid: 2.1.8 dayjs: 1.11.0 @@ -35,8 +36,9 @@ specifiers: js-yaml: 4.1.0 jsonwebtoken: 8.5.1 lint-staged: 12.3.7 - mustache: ^4.2.0 + mustache: 4.2.0 node-forge: 1.3.0 + p-limit: 4.0.0 postcss: 8.4.12 prettier: 2.6.1 prettier-plugin-svelte: 2.6.0 @@ -46,9 +48,9 @@ specifiers: svelte-check: 2.4.6 svelte-kit-cookie-session: 2.1.2 svelte-preprocess: 4.10.4 - svelte-select: ^4.4.7 + svelte-select: 4.4.7 tailwindcss: 3.0.23 - tailwindcss-scrollbar: ^0.1.0 + tailwindcss-scrollbar: 0.1.0 ts-node: 10.7.0 tslib: 2.3.1 typescript: 4.6.3 @@ -75,6 +77,7 @@ dependencies: jsonwebtoken: 8.5.1 mustache: 4.2.0 node-forge: 1.3.0 + p-limit: 4.0.0 svelte-kit-cookie-session: 2.1.2 tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.23 unique-names-generator: 4.7.1 @@ -90,6 +93,7 @@ devDependencies: '@typescript-eslint/parser': 4.31.1_eslint@7.32.0+typescript@4.6.3 '@zerodevx/svelte-toast': 0.7.1 autoprefixer: 10.4.4_postcss@8.4.12 + cross-env: 7.0.3 cross-var: 1.1.0 eslint: 7.32.0 eslint-config-prettier: 8.5.0_eslint@7.32.0 @@ -1991,6 +1995,17 @@ packages: luxon: 1.28.0 dev: false + /cross-env/7.0.3: + resolution: + { + integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + } + engines: { node: '>=10.14', npm: '>=6', yarn: '>=1' } + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + /cross-spawn/5.1.0: resolution: { integrity: sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= } dependencies: @@ -4016,6 +4031,16 @@ packages: engines: { node: '>=12.20' } dev: false + /p-limit/4.0.0: + resolution: + { + integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dependencies: + yocto-queue: 1.0.0 + dev: false + /p-map/2.1.0: resolution: { @@ -5392,3 +5417,11 @@ packages: } engines: { node: '>=6' } dev: true + + /yocto-queue/1.0.0: + resolution: + { + integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + } + engines: { node: '>=12.20' } + dev: false diff --git a/prisma/migrations/20220402135305_python/migration.sql b/prisma/migrations/20220402135305_python/migration.sql new file mode 100644 index 000000000..0c253d539 --- /dev/null +++ b/prisma/migrations/20220402135305_python/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "pythonModule" TEXT; +ALTER TABLE "Application" ADD COLUMN "pythonVariable" TEXT; +ALTER TABLE "Application" ADD COLUMN "pythonWSGI" TEXT; diff --git a/prisma/migrations/20220402210645_meilisearch/migration.sql b/prisma/migrations/20220402210645_meilisearch/migration.sql new file mode 100644 index 000000000..9e832b107 --- /dev/null +++ b/prisma/migrations/20220402210645_meilisearch/migration.sql @@ -0,0 +1,12 @@ +-- CreateTable +CREATE TABLE "MeiliSearch" ( + "id" TEXT NOT NULL PRIMARY KEY, + "masterKey" TEXT NOT NULL, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "MeiliSearch_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "MeiliSearch_serviceId_key" ON "MeiliSearch"("serviceId"); diff --git a/prisma/migrations/20220405151428_wordpress_sftp/migration.sql b/prisma/migrations/20220405151428_wordpress_sftp/migration.sql new file mode 100644 index 000000000..6c3c4b907 --- /dev/null +++ b/prisma/migrations/20220405151428_wordpress_sftp/migration.sql @@ -0,0 +1,29 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Wordpress" ( + "id" TEXT NOT NULL PRIMARY KEY, + "extraConfig" TEXT, + "tablePrefix" TEXT, + "mysqlUser" TEXT NOT NULL, + "mysqlPassword" TEXT NOT NULL, + "mysqlRootUser" TEXT NOT NULL, + "mysqlRootUserPassword" TEXT NOT NULL, + "mysqlDatabase" TEXT, + "mysqlPublicPort" INTEGER, + "ftpEnabled" BOOLEAN NOT NULL DEFAULT false, + "ftpUser" TEXT, + "ftpPassword" TEXT, + "ftpPublicPort" INTEGER, + "ftpHostKey" TEXT, + "ftpHostKeyPrivate" TEXT, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress"; +DROP TABLE "Wordpress"; +ALTER TABLE "new_Wordpress" RENAME TO "Wordpress"; +CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/prisma/migrations/20220407220809_unique_storage_fix/migration.sql b/prisma/migrations/20220407220809_unique_storage_fix/migration.sql new file mode 100644 index 000000000..9b645cc3b --- /dev/null +++ b/prisma/migrations/20220407220809_unique_storage_fix/migration.sql @@ -0,0 +1,5 @@ +-- DropIndex +DROP INDEX "ApplicationPersistentStorage_path_key"; + +-- DropIndex +DROP INDEX "ApplicationPersistentStorage_applicationId_key"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b05a6a6a4..928562abd 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -88,6 +88,9 @@ model Application { baseDirectory String? publishDirectory String? phpModules String? + pythonWSGI String? + pythonModule String? + pythonVariable String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt settings ApplicationSettings? @@ -115,8 +118,8 @@ model ApplicationSettings { model ApplicationPersistentStorage { id String @id @default(cuid()) application Application @relation(fields: [applicationId], references: [id]) - applicationId String @unique - path String @unique + applicationId String + path String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -281,6 +284,7 @@ model Service { wordpress Wordpress? ghost Ghost? serviceSecret ServiceSecret[] + meiliSearch MeiliSearch? } model PlausibleAnalytics { @@ -329,6 +333,12 @@ model Wordpress { mysqlRootUserPassword String mysqlDatabase String? mysqlPublicPort Int? + ftpEnabled Boolean @default(false) + ftpUser String? + ftpPassword String? + ftpPublicPort Int? + ftpHostKey String? + ftpHostKeyPrivate String? serviceId String @unique service Service @relation(fields: [serviceId], references: [id]) createdAt DateTime @default(now()) @@ -350,3 +360,12 @@ model Ghost { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } + +model MeiliSearch { + id String @id @default(cuid()) + masterKey String + serviceId String @unique + service Service @relation(fields: [serviceId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} diff --git a/src/app.d.ts b/src/app.d.ts index 0557299b9..d432aef4a 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -15,18 +15,20 @@ declare namespace App { readOnly: boolean; source: string; settings: string; + database: Record; + versions: string; + privatePort: string; } } interface SessionData { + whiteLabeled: boolean; version?: string; userId?: string | null; teamId?: string | null; permission?: string; isAdmin?: boolean; expires?: string | null; - gitlabToken?: string | null; - ghToken?: string | null; } type DateTimeFormatOptions = { diff --git a/src/app.html b/src/app.html index 2dc97f37d..a0336e87d 100644 --- a/src/app.html +++ b/src/app.html @@ -2,7 +2,6 @@ - Coolify %svelte.head% diff --git a/src/hooks.ts b/src/hooks.ts index d6c6eca6d..c72c7fd6c 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -7,6 +7,8 @@ import { version } from '$lib/common'; import cookie from 'cookie'; import { dev } from '$app/env'; +const whiteLabeled = process.env['COOLIFY_WHITE_LABELED'] === 'true'; + export const handle = handleSession( { secret: process.env['COOLIFY_SECRET_KEY'], @@ -71,6 +73,7 @@ export const handle = handleSession( export const getSession: GetSession = function ({ locals }) { return { version, + whiteLabeled, ...locals.session.data }; }; diff --git a/src/lib/buildPacks/common.ts b/src/lib/buildPacks/common.ts index 3f8b4b43e..65f4d6903 100644 --- a/src/lib/buildPacks/common.ts +++ b/src/lib/buildPacks/common.ts @@ -100,6 +100,7 @@ export const setDefaultConfiguration = async (data) => { if (buildPack === 'static') port = 80; else if (buildPack === 'node') port = 3000; else if (buildPack === 'php') port = 80; + else if (buildPack === 'python') port = 8000; } if (!installCommand) installCommand = template?.installCommand || 'yarn install'; if (!startCommand) startCommand = template?.startCommand || 'yarn start'; @@ -123,20 +124,13 @@ export const setDefaultConfiguration = async (data) => { export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId) { try { - // TODO: Write full .dockerignore for all deployments!! if (buildPack === 'php') { - await fs.writeFile( - `${workdir}/.htaccess`, - ` - RewriteEngine On - RewriteBase / - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.+)$ index.php [QSA,L] - ` - ); await fs.writeFile(`${workdir}/entrypoint.sh`, `chown -R 1000 /app`); - saveBuildLog({ line: 'Copied default configuration file for PHP.', buildId, applicationId }); + await saveBuildLog({ + line: 'Copied default configuration file for PHP.', + buildId, + applicationId + }); } else if (staticDeployments.includes(buildPack)) { await fs.writeFile( `${workdir}/nginx.conf`, @@ -190,7 +184,7 @@ export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, ap } ` ); - saveBuildLog({ line: 'Copied default configuration file.', buildId, applicationId }); + await saveBuildLog({ line: 'Copied default configuration file.', buildId, applicationId }); } } catch (error) { console.log(error); diff --git a/src/lib/buildPacks/docker.ts b/src/lib/buildPacks/docker.ts index 2c86bb532..371ffed82 100644 --- a/src/lib/buildPacks/docker.ts +++ b/src/lib/buildPacks/docker.ts @@ -28,11 +28,11 @@ export default async function ({ if (secret.isBuildSecret) { if (pullmergeRequestId) { if (secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } else { if (!secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } } diff --git a/src/lib/buildPacks/index.ts b/src/lib/buildPacks/index.ts index 41a7655f9..babbe8f17 100644 --- a/src/lib/buildPacks/index.ts +++ b/src/lib/buildPacks/index.ts @@ -12,6 +12,7 @@ import php from './php'; import rust from './rust'; import astro from './static'; import eleventy from './static'; +import python from './python'; export { node, @@ -27,5 +28,6 @@ export { php, rust, astro, - eleventy + eleventy, + python }; diff --git a/src/lib/buildPacks/nextjs.ts b/src/lib/buildPacks/nextjs.ts index 4f1f3b0d6..0f58b3b84 100644 --- a/src/lib/buildPacks/nextjs.ts +++ b/src/lib/buildPacks/nextjs.ts @@ -23,11 +23,11 @@ const createDockerfile = async (data, image): Promise => { if (secret.isBuildSecret) { if (pullmergeRequestId) { if (secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } else { if (!secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } } diff --git a/src/lib/buildPacks/node.ts b/src/lib/buildPacks/node.ts index 54527a728..869e28b5d 100644 --- a/src/lib/buildPacks/node.ts +++ b/src/lib/buildPacks/node.ts @@ -24,11 +24,11 @@ const createDockerfile = async (data, image): Promise => { if (secret.isBuildSecret) { if (pullmergeRequestId) { if (secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } else { if (!secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } } diff --git a/src/lib/buildPacks/nuxtjs.ts b/src/lib/buildPacks/nuxtjs.ts index 207afe864..bfa48bf73 100644 --- a/src/lib/buildPacks/nuxtjs.ts +++ b/src/lib/buildPacks/nuxtjs.ts @@ -23,11 +23,11 @@ const createDockerfile = async (data, image): Promise => { if (secret.isBuildSecret) { if (pullmergeRequestId) { if (secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } else { if (!secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } } diff --git a/src/lib/buildPacks/php.ts b/src/lib/buildPacks/php.ts index abec33b3d..cfb39d20a 100644 --- a/src/lib/buildPacks/php.ts +++ b/src/lib/buildPacks/php.ts @@ -1,23 +1,45 @@ import { buildImage } from '$lib/docker'; import { promises as fs } from 'fs'; -const createDockerfile = async (data, image): Promise => { +const createDockerfile = async (data, image, htaccessFound): Promise => { const { workdir, baseDirectory } = data; const Dockerfile: Array = []; + let composerFound = false; + try { + await fs.readFile(`${workdir}${baseDirectory || ''}/composer.json`); + composerFound = true; + } catch (error) {} + Dockerfile.push(`FROM ${image}`); Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push('WORKDIR /app'); Dockerfile.push(`COPY .${baseDirectory || ''} /app`); - Dockerfile.push(`COPY /.htaccess .`); + if (htaccessFound) { + Dockerfile.push(`COPY .${baseDirectory || ''}/.htaccess ./`); + } + if (composerFound) { + Dockerfile.push(`RUN composer install`); + } + Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`); Dockerfile.push(`EXPOSE 80`); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); }; export default async function (data) { + const { workdir, baseDirectory } = data; try { - const image = 'webdevops/php-nginx'; - await createDockerfile(data, image); + let htaccessFound = false; + try { + await fs.readFile(`${workdir}${baseDirectory || ''}/.htaccess`); + htaccessFound = true; + } catch (e) { + // + } + const image = htaccessFound + ? 'webdevops/php-apache:8.0-alpine' + : 'webdevops/php-nginx:8.0-alpine'; + await createDockerfile(data, image, htaccessFound); await buildImage(data); } catch (error) { throw error; diff --git a/src/lib/buildPacks/python.ts b/src/lib/buildPacks/python.ts new file mode 100644 index 000000000..1c6bdf6bf --- /dev/null +++ b/src/lib/buildPacks/python.ts @@ -0,0 +1,71 @@ +import { buildImage } from '$lib/docker'; +import { promises as fs } from 'fs'; + +const createDockerfile = async (data, image): Promise => { + const { + workdir, + port, + baseDirectory, + secrets, + pullmergeRequestId, + pythonWSGI, + pythonModule, + pythonVariable + } = data; + const Dockerfile: Array = []; + Dockerfile.push(`FROM ${image}`); + Dockerfile.push('WORKDIR /app'); + Dockerfile.push(`LABEL coolify.image=true`); + if (secrets.length > 0) { + secrets.forEach((secret) => { + if (secret.isBuildSecret) { + if (pullmergeRequestId) { + if (secret.isPRMRSecret) { + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); + } + } else { + if (!secret.isPRMRSecret) { + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); + } + } + } + }); + } + if (pythonWSGI?.toLowerCase() === 'gunicorn') { + Dockerfile.push(`RUN pip install gunicorn`); + } else if (pythonWSGI?.toLowerCase() === 'uwsgi') { + Dockerfile.push(`RUN apk add --no-cache uwsgi-python3`); + // Dockerfile.push(`RUN pip install --no-cache-dir uwsgi`) + } + + try { + await fs.stat(`${workdir}${baseDirectory || ''}/requirements.txt`); + Dockerfile.push(`COPY .${baseDirectory || ''}/requirements.txt ./`); + Dockerfile.push(`RUN pip install --no-cache-dir -r .${baseDirectory || ''}/requirements.txt`); + } catch (e) { + // + } + Dockerfile.push(`COPY .${baseDirectory || ''} ./`); + Dockerfile.push(`EXPOSE ${port}`); + if (pythonWSGI?.toLowerCase() === 'gunicorn') { + Dockerfile.push(`CMD gunicorn -w=4 -b=0.0.0.0:8000 ${pythonModule}:${pythonVariable}`); + } else if (pythonWSGI?.toLowerCase() === 'uwsgi') { + Dockerfile.push( + `CMD uwsgi --master -p 4 --http-socket 0.0.0.0:8000 --uid uwsgi --plugins python3 --protocol uwsgi --wsgi ${pythonModule}:${pythonVariable}` + ); + } else { + Dockerfile.push(`CMD python ${pythonModule}`); + } + + await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); +}; + +export default async function (data) { + try { + const image = 'python:3-alpine'; + await createDockerfile(data, image); + await buildImage(data); + } catch (error) { + throw error; + } +} diff --git a/src/lib/buildPacks/static.ts b/src/lib/buildPacks/static.ts index 17900ced3..e9e7179d5 100644 --- a/src/lib/buildPacks/static.ts +++ b/src/lib/buildPacks/static.ts @@ -22,11 +22,11 @@ const createDockerfile = async (data, image): Promise => { if (secret.isBuildSecret) { if (pullmergeRequestId) { if (secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } else { if (!secret.isPRMRSecret) { - Dockerfile.push(`ARG ${secret.name} ${secret.value}`); + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); } } } diff --git a/src/lib/common.ts b/src/lib/common.ts index 68f85572f..57cdabba2 100644 --- a/src/lib/common.ts +++ b/src/lib/common.ts @@ -46,13 +46,20 @@ const customConfig: Config = { export const version = currentVersion; export const asyncExecShell = util.promisify(child.exec); export const asyncSleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)); + export const sentry = Sentry; export const uniqueName = () => uniqueNamesGenerator(customConfig); export const saveBuildLog = async ({ line, buildId, applicationId }) => { - const addTimestamp = `${generateTimestamp()} ${line}`; - return await buildLogQueue.add(buildId, { buildId, line: addTimestamp, applicationId }); + if (line) { + if (line.includes('ghs_')) { + const regex = /ghs_.*@/g; + line = line.replace(regex, '@'); + } + const addTimestamp = `${generateTimestamp()} ${line}`; + return await buildLogQueue.add(buildId, { buildId, line: addTimestamp, applicationId }); + } }; export const isTeamIdTokenAvailable = (request) => { @@ -80,7 +87,7 @@ export const getTeam = (event) => { export const getUserDetails = async (event, isAdminRequired = true) => { const teamId = getTeam(event); - const userId = event.locals.session.data.userId || null; + const userId = event?.locals?.session?.data?.userId || null; const { permission = 'read' } = await db.prisma.permission.findFirst({ where: { teamId, userId }, select: { permission: true }, @@ -95,6 +102,7 @@ export const getUserDetails = async (event, isAdminRequired = true) => { message: 'OK' } }; + if (isAdminRequired && permission !== 'admin' && permission !== 'owner') { payload.status = 401; payload.body.message = diff --git a/src/lib/components/DatabaseLinks.svelte b/src/lib/components/DatabaseLinks.svelte new file mode 100644 index 000000000..9ef11a238 --- /dev/null +++ b/src/lib/components/DatabaseLinks.svelte @@ -0,0 +1,25 @@ + + + + {#if database.type === 'clickhouse'} + + {:else if database.type === 'couchdb'} + + {:else if database.type === 'mongodb'} + + {:else if database.type === 'mysql'} + + {:else if database.type === 'postgresql'} + + {:else if database.type === 'redis'} + + {/if} + diff --git a/src/lib/components/ServiceLinks.svelte b/src/lib/components/ServiceLinks.svelte new file mode 100644 index 000000000..a3b4ce2cd --- /dev/null +++ b/src/lib/components/ServiceLinks.svelte @@ -0,0 +1,55 @@ + + +{#if service.type === 'plausibleanalytics'} + + + +{:else if service.type === 'nocodb'} + + + +{:else if service.type === 'minio'} + + + +{:else if service.type === 'vscodeserver'} + + + +{:else if service.type === 'wordpress'} + + + +{:else if service.type === 'vaultwarden'} + + + +{:else if service.type === 'languagetool'} + + + +{:else if service.type === 'n8n'} + + + +{:else if service.type === 'uptimekuma'} + + + +{:else if service.type === 'ghost'} + + + +{/if} diff --git a/src/lib/components/Setting.svelte b/src/lib/components/Setting.svelte index c8764bed7..d7b028861 100644 --- a/src/lib/components/Setting.svelte +++ b/src/lib/components/Setting.svelte @@ -7,6 +7,7 @@ export let isCenter = true; export let disabled = false; export let dataTooltip = null; + export let loading = false;
@@ -26,9 +27,10 @@ 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} - class:bg-green-600={setting} - class:bg-stone-700={!setting} + class:opacity-50={disabled || loading} + class:bg-green-600={!loading && setting} + class:bg-stone-700={!loading && !setting} + class:bg-yellow-500={loading} > Use setting