diff --git a/.dockerignore b/.dockerignore index 66ff0bf1f..1b0bcf1c2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,7 +8,6 @@ package .env.* !.env.example dist -client apps/api/db/*.db local-serve apps/api/db/migration.db-journal diff --git a/.gitignore b/.gitignore index c31810490..40773d23f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ package .env.* !.env.example dist -client apps/api/db/*.db apps/api/db/migration.db-journal apps/api/core* @@ -17,4 +16,8 @@ apps/backup/backups/* logs others/certificates backups/* -!backups/.gitkeep \ No newline at end of file +!backups/.gitkeep + +# Trpc +apps/server/db/*.db +apps/server/db/*.db-journal \ No newline at end of file diff --git a/apps/api/db/dev.db.bak b/apps/api/db/dev.db.bak new file mode 100644 index 000000000..2a6385b11 Binary files /dev/null and b/apps/api/db/dev.db.bak differ diff --git a/apps/api/devTemplates.yaml b/apps/api/devTemplates.yaml index 80ec2f699..5e7268920 100644 --- a/apps/api/devTemplates.yaml +++ b/apps/api/devTemplates.yaml @@ -2075,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, @@ -2088,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, @@ -2100,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, @@ -2117,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, @@ -2130,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 @@ -2282,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, @@ -2295,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, @@ -2307,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, @@ -2324,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, @@ -2337,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 diff --git a/apps/api/package.json b/apps/api/package.json index ad987b719..59d0c1855 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -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" - } -} \ No newline at end of file + "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" + } +} diff --git a/apps/api/src/jobs/deployApplication.ts b/apps/api/src/jobs/deployApplication.ts index 90afea210..7f0977043 100644 --- a/apps/api/src/jobs/deployApplication.ts +++ b/apps/api/src/jobs/deployApplication.ts @@ -3,8 +3,25 @@ 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 +} from '../lib/common'; import * as importers from '../lib/importers'; import * as buildpacks from '../lib/buildPacks'; @@ -14,33 +31,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,60 +92,74 @@ 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}` - ]; + const envs = [`PORT='${port}'`]; if (secrets.length > 0) { secrets.forEach((secret) => { if (pullmergeRequestId) { - const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret) + const isSecretFound = secrets.filter( + (s) => s.name === secret.name && s.isPRMRSecret + ); if (isSecretFound.length > 0) { - envs.push(`${secret.name}=${isSecretFound[0].value}`); + envs.push(`${secret.name}='${isSecretFound[0].value}'`); } else { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } else { if (!secret.isPRMRSecret) { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } }); } await fs.writeFile(`${workdir}/.env`, envs.join('\n')); - let envFound = false; try { envFound = !!(await fs.stat(`${workdir}/.env`)); @@ -116,14 +169,14 @@ import * as buildpacks from '../lib/buildPacks'; 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,7 +191,7 @@ import * as buildpacks from '../lib/buildPacks'; services: { [applicationId]: { build: { - context: workdir, + context: workdir }, image: `${applicationId}:${buildId}`, container_name: applicationId, @@ -148,7 +201,7 @@ import * as buildpacks from '../lib/buildPacks'; depends_on: [], expose: [port], ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), - ...defaultComposeConfiguration(destinationDocker.network), + ...defaultComposeConfiguration(destinationDocker.network) } }, networks: { @@ -159,11 +212,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 +231,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 +246,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 +259,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 +276,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 +308,7 @@ import * as buildpacks from '../lib/buildPacks'; deploymentType, gitCommitHash, dockerRegistry - } = application + } = application; let { branch, @@ -257,7 +324,7 @@ import * as buildpacks from '../lib/buildPacks'; dockerComposeFileLocation, dockerComposeConfiguration, denoMainFile - } = application + } = application; let imageId = applicationId; let domain = getDomain(fqdn); @@ -272,9 +339,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 +354,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 +396,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 +413,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); @@ -381,10 +458,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 +471,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 +494,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 +549,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 +590,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 +623,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 +643,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,48 +672,55 @@ 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}` - ]; + const envs = [`PORT='${port}'`]; if (secrets.length > 0) { secrets.forEach((secret) => { if (pullmergeRequestId) { - const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret) + const isSecretFound = secrets.filter( + (s) => s.name === secret.name && s.isPRMRSecret + ); if (isSecretFound.length > 0) { - envs.push(`${secret.name}=${isSecretFound[0].value}`); + envs.push(`${secret.name}='${isSecretFound[0].value}'`); } else { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } else { if (!secret.isPRMRSecret) { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } }); } await fs.writeFile(`${workdir}/.env`, envs.join('\n')); if (dockerRegistry) { - const { url, username, password } = dockerRegistry - await saveDockerRegistryCredentials({ url, username, password, workdir }) + const { url, username, password } = dockerRegistry; + await saveDockerRegistryCredentials({ url, username, password, workdir }); } let envFound = false; @@ -621,7 +749,7 @@ import * as buildpacks from '../lib/buildPacks'; depends_on: [], expose: [port], ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), - ...defaultComposeConfiguration(destinationDocker.network), + ...defaultComposeConfiguration(destinationDocker.network) } }, networks: { @@ -632,11 +760,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 +780,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 +801,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 +827,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); })(); diff --git a/apps/api/src/lib/buildPacks/common.ts b/apps/api/src/lib/buildPacks/common.ts index ba50b32b4..f25e15e7d 100644 --- a/apps/api/src/lib/buildPacks/common.ts +++ b/apps/api/src/lib/buildPacks/common.ts @@ -1,6 +1,17 @@ -import { base64Encode, decrypt, encrypt, executeCommand, generateTimestamp, getDomain, isARM, isDev, prisma, version } from "../common"; +import { + base64Encode, + decrypt, + encrypt, + executeCommand, + 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 +28,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 +330,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 +341,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 +355,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 +375,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 +431,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 +441,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 +508,6 @@ export const scanningTemplates = { } }; - export const saveBuildLog = async ({ line, buildId, @@ -491,7 +519,7 @@ export const saveBuildLog = async ({ }): Promise => { 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 +532,11 @@ export const saveBuildLog = async ({ line = line.replace(regex, '@'); } 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 +546,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 +644,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); @@ -622,14 +656,14 @@ export async function saveDockerRegistryCredentials({ url, username, password, w 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 +674,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 }) { @@ -744,15 +790,15 @@ export async function buildCacheImageWithNode(data, imageForBuild) { secrets.forEach((secret) => { if (secret.isBuildSecret) { if (pullmergeRequestId) { - const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret) + const isSecretFound = secrets.filter((s) => s.name === secret.name && s.isPRMRSecret); if (isSecretFound.length > 0) { - Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`); + Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`); } else { - 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}'`); } } } @@ -782,15 +828,15 @@ export async function buildCacheImageForLaravel(data, imageForBuild) { secrets.forEach((secret) => { if (secret.isBuildSecret) { if (pullmergeRequestId) { - const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret) + const isSecretFound = secrets.filter((s) => s.name === secret.name && s.isPRMRSecret); if (isSecretFound.length > 0) { - Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`); + Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`); } else { - 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}'`); } } } @@ -804,11 +850,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 = []; Dockerfile.push(`FROM ${imageForBuild} as planner-${applicationId}`); diff --git a/apps/api/src/lib/buildPacks/compose.ts b/apps/api/src/lib/buildPacks/compose.ts index f180630dc..bda83bd9e 100644 --- a/apps/api/src/lib/buildPacks/compose.ts +++ b/apps/api/src/lib/buildPacks/compose.ts @@ -31,13 +31,13 @@ export default async function (data) { if (pullmergeRequestId) { const isSecretFound = secrets.filter((s) => s.name === secret.name && s.isPRMRSecret); if (isSecretFound.length > 0) { - envs.push(`${secret.name}=${isSecretFound[0].value}`); + envs.push(`${secret.name}='${isSecretFound[0].value}'`); } else { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } else { if (!secret.isPRMRSecret) { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } }); diff --git a/apps/api/src/lib/buildPacks/deno.ts b/apps/api/src/lib/buildPacks/deno.ts index 074464445..90107e4d2 100644 --- a/apps/api/src/lib/buildPacks/deno.ts +++ b/apps/api/src/lib/buildPacks/deno.ts @@ -29,13 +29,13 @@ const createDockerfile = async (data, image): Promise => { 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}`); + Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`); } else { - 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/apps/api/src/lib/buildPacks/docker.ts b/apps/api/src/lib/buildPacks/docker.ts index 902dd449b..2ef265fe3 100644 --- a/apps/api/src/lib/buildPacks/docker.ts +++ b/apps/api/src/lib/buildPacks/docker.ts @@ -2,24 +2,11 @@ import { promises as fs } from 'fs'; 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 = DockerfileRaw - .toString() - .trim() - .split('\n'); + const DockerfileRaw = await fs.readFile(`${file}`, 'utf8'); + const Dockerfile: Array = DockerfileRaw.toString().trim().split('\n'); Dockerfile.forEach((line, index) => { if (line.startsWith('FROM')) { Dockerfile.splice(index + 1, 0, `LABEL coolify.buildId=${buildId}`); @@ -34,14 +21,13 @@ export default async function (data) { ) { Dockerfile.forEach((line, index) => { if (line.startsWith('FROM')) { - Dockerfile.splice(index + 1, 0, `ARG ${secret.name}=${secret.value}`); + Dockerfile.splice(index + 1, 0, `ARG ${secret.name}='${secret.value}'`); } }); } } }); } - - await fs.writeFile(`${workdir}${dockerFileLocation}`, Dockerfile.join('\n')); + await fs.writeFile(`${data.workdir}${dockerFileLocation}`, Dockerfile.join('\n')); await buildImage(data); } diff --git a/apps/api/src/lib/buildPacks/nextjs.ts b/apps/api/src/lib/buildPacks/nextjs.ts index 90a70f89b..c7aaa9dcd 100644 --- a/apps/api/src/lib/buildPacks/nextjs.ts +++ b/apps/api/src/lib/buildPacks/nextjs.ts @@ -29,13 +29,13 @@ const createDockerfile = async (data, image): Promise => { 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}`); + Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`); } else { - 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/apps/api/src/lib/buildPacks/node.ts b/apps/api/src/lib/buildPacks/node.ts index 546942542..c2e77c9f5 100644 --- a/apps/api/src/lib/buildPacks/node.ts +++ b/apps/api/src/lib/buildPacks/node.ts @@ -23,15 +23,15 @@ const createDockerfile = async (data, image): Promise => { secrets.forEach((secret) => { if (secret.isBuildSecret) { if (pullmergeRequestId) { - const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret) + const isSecretFound = secrets.filter((s) => s.name === secret.name && s.isPRMRSecret); if (isSecretFound.length > 0) { - Dockerfile.push(`ARG ${secret.name}=${isSecretFound[0].value}`); + Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`); } else { - 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/apps/api/src/lib/buildPacks/nuxtjs.ts b/apps/api/src/lib/buildPacks/nuxtjs.ts index 90a70f89b..c7aaa9dcd 100644 --- a/apps/api/src/lib/buildPacks/nuxtjs.ts +++ b/apps/api/src/lib/buildPacks/nuxtjs.ts @@ -29,13 +29,13 @@ const createDockerfile = async (data, image): Promise => { 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}`); + Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`); } else { - 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/apps/api/src/lib/buildPacks/php.ts b/apps/api/src/lib/buildPacks/php.ts index eaf97d1b1..a5e2737ac 100644 --- a/apps/api/src/lib/buildPacks/php.ts +++ b/apps/api/src/lib/buildPacks/php.ts @@ -18,13 +18,13 @@ const createDockerfile = async (data, image, htaccessFound): Promise => { 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}`); + Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`); } else { - 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/apps/api/src/lib/buildPacks/python.ts b/apps/api/src/lib/buildPacks/python.ts index 36d707f16..3b5dfc264 100644 --- a/apps/api/src/lib/buildPacks/python.ts +++ b/apps/api/src/lib/buildPacks/python.ts @@ -23,13 +23,13 @@ const createDockerfile = async (data, image): Promise => { 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}`); + Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`); } else { - 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/apps/api/src/lib/buildPacks/static.ts b/apps/api/src/lib/buildPacks/static.ts index 160098654..235175c7a 100644 --- a/apps/api/src/lib/buildPacks/static.ts +++ b/apps/api/src/lib/buildPacks/static.ts @@ -30,13 +30,13 @@ const createDockerfile = async (data, image): Promise => { 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}`); + Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`); } else { - 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/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index 5ca17eaf2..52dd2dd53 100644 --- a/apps/api/src/lib/common.ts +++ b/apps/api/src/lib/common.ts @@ -19,7 +19,7 @@ import { saveBuildLog, saveDockerRegistryCredentials } from './buildPacks/common import { scheduler } from './scheduler'; import type { ExecaChildProcess } from 'execa'; -export const version = '3.12.1'; +export const version = '3.12.2'; export const isDev = process.env.NODE_ENV === 'development'; export const sentryDSN = 'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216'; diff --git a/apps/api/src/lib/services.ts b/apps/api/src/lib/services.ts index b423cbe9c..19e3094eb 100644 --- a/apps/api/src/lib/services.ts +++ b/apps/api/src/lib/services.ts @@ -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 []; + } } diff --git a/apps/api/src/routes/api/v1/applications/handlers.ts b/apps/api/src/routes/api/v1/applications/handlers.ts index f43a5c536..3e2739ec5 100644 --- a/apps/api/src/routes/api/v1/applications/handlers.ts +++ b/apps/api/src/routes/api/v1/applications/handlers.ts @@ -468,13 +468,13 @@ export async function restartApplication(request: FastifyRequest s.name === secret.name && s.isPRMRSecret) if (isSecretFound.length > 0) { - envs.push(`${secret.name}=${isSecretFound[0].value}`); + envs.push(`${secret.name}='${isSecretFound[0].value}'`); } else { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } else { if (!secret.isPRMRSecret) { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } }); @@ -1169,13 +1169,13 @@ export async function restartPreview(request: FastifyRequest s.name === secret.name && s.isPRMRSecret) if (isSecretFound.length > 0) { - envs.push(`${secret.name}=${isSecretFound[0].value}`); + envs.push(`${secret.name}='${isSecretFound[0].value}'`); } else { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } else { if (!secret.isPRMRSecret) { - envs.push(`${secret.name}=${secret.value}`); + envs.push(`${secret.name}='${secret.value}'`); } } }); diff --git a/apps/api/src/routes/api/v1/base/index.ts b/apps/api/src/routes/api/v1/base/index.ts index 170a138f3..d631df3d8 100644 --- a/apps/api/src/routes/api/v1/base/index.ts +++ b/apps/api/src/routes/api/v1/base/index.ts @@ -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 => { - 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; diff --git a/apps/api/templates.json b/apps/api/templates.json index 3e57912eb..8c3230bf4 100644 --- a/apps/api/templates.json +++ b/apps/api/templates.json @@ -1 +1 @@ -[{"templateVersion":"1.0.0","defaultVersion":"0.8.0","documentation":"https://pocketbase.io/docs/","type":"pocketbase","name":"Pocketbase","description":"Open Source realtime backend in 1 file","services":{"$$id":{"image":"coollabsio/pocketbase:$$core_version","volumes":["$$id-data:/app/pb_data"],"ports":["8080"]}}},{"templateVersion":"1.0.0","defaultVersion":"v1.5.1","documentation":"https://plausible.io/doc/","type":"plausibleanalytics-arm","name":"Plausible Analytics (ARM)","description":"A lightweight and open-source website analytics tool.","labels":["analytics","statistics","plausible","gdpr","no-cookie","google analytics"],"services":{"$$id":{"name":"Plausible Analytics","command":"sh -c \"sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run\"","depends_on":["$$id-postgresql","$$id-clickhouse"],"image":"plausible/analytics:$$core_version","environment":["ADMIN_USER_EMAIL=$$config_admin_user_email","ADMIN_USER_NAME=$$config_admin_user_name","ADMIN_USER_PWD=$$secret_admin_user_pwd","BASE_URL=$$config_base_url","SECRET_KEY_BASE=$$secret_secret_key_base","DISABLE_AUTH=$$config_disable_auth","DISABLE_REGISTRATION=$$config_disable_registration","DATABASE_URL=$$secret_database_url","CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url"],"ports":["8000"]},"$$id-postgresql":{"name":"PostgreSQL","image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_USER=$$config_postgres_user","POSTGRES_DB=$$config_postgres_db"]},"$$id-clickhouse":{"name":"Clickhouse","volumes":["$$id-clickhouse-data:/var/lib/clickhouse"],"image":"clickhouse/clickhouse-server:22.6-alpine","ulimits":{"nofile":{"soft":262144,"hard":262144}},"files":[{"location":"/etc/clickhouse-server/users.d/logging.xml","content":"warningtrue"},{"location":"/etc/clickhouse-server/config.d/logging.xml","content":"00"},{"location":"/docker-entrypoint-initdb.d/init.query","content":"CREATE DATABASE IF NOT EXISTS plausible;"},{"location":"/docker-entrypoint-initdb.d/init-db.sh","content":"clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query"}]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":"You must set this to the FQDN of the Plausible Analytics instance. This is used to generate the links to the Plausible Analytics instance."},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_clickhouse_database_url","name":"CLICKHOUSE_DATABASE_URL","label":"Database URL for Clickhouse","defaultValue":"http://$$id-clickhouse:8123/plausible","description":""},{"id":"$$config_admin_user_email","name":"ADMIN_USER_EMAIL","label":"Admin Email Address","defaultValue":"admin@example.com","description":"This is the admin email. Please change it."},{"id":"$$config_admin_user_name","name":"ADMIN_USER_NAME","label":"Admin User Name","defaultValue":"$$generate_username","description":"This is the admin username. Please change it."},{"id":"$$secret_admin_user_pwd","name":"ADMIN_USER_PWD","label":"Admin User Password","defaultValue":"$$generate_password","description":"This is the admin password. Please change it.","showOnConfiguration":true},{"id":"$$secret_secret_key_base","name":"SECRET_KEY_BASE","label":"Secret Key Base","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_disable_auth","name":"DISABLE_AUTH","label":"Disable Authentication","defaultValue":"false","description":""},{"id":"$$config_disable_registration","name":"DISABLE_REGISTRATION","label":"Disable Registration","defaultValue":"true","description":""},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL Username","defaultValue":"postgresql","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"plausible","description":""},{"id":"$$config_scriptName","name":"SCRIPT_NAME","label":"Custom Script Name","defaultValue":"plausible.js","description":"This is the default script name."}]},{"templateVersion":"1.0.0","defaultVersion":"1.17","documentation":"https://docs.gitea.io","type":"gitea","name":"Gitea","description":"Gitea is a community managed lightweight code hosting solution written in Go.","labels":["storage","git"],"services":{"$$id":{"name":"Gitea","documentation":"https://docs.gitea.io","image":"gitea/gitea:$$core_version","volumes":["$$id-data:/data","/etc/timezone:/etc/timezone:ro","/etc/localtime:/etc/localtime:ro"],"environment":["USER_UID=1000","USER_GID=1000","DOMAIN=$$config_domain","SSH_DOMAIN=$$config_ssh_domain","ROOT_URL=$$config_root_url","SECRET_KEY=$$secret_secret_key","INTERNAL_TOKEN=$$secret_internal_token","SSH_PORT=22","START_SSH_SERVER=$$config_start_ssh_server"],"ports":["3000","22"],"proxy":[{"port":"22","hostPort":"$$config_hostport_ssh"}]}},"variables":[{"id":"$$config_hostport_ssh","name":"SSH_PORT","label":"SSH Port","defaultValue":"8022","description":"","required":true},{"id":"$$config_domain","name":"DOMAIN","label":"Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_ssh_domain","name":"SSH_DOMAIN","label":"SSH Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_start_ssh_server","name":"START_SSH_SERVER","label":"Start SSH Server","defaultValue":"true","description":""},{"id":"$$config_root_url","name":"ROOT_URL","label":"Root URL of Gitea","defaultValue":"$$generate_fqdn_slash","description":""},{"id":"$$secret_secret_key","name":"SECRET_KEY","label":"Secret Key","defaultValue":"$$generate_hex(32)","description":""},{"id":"$$secret_internal_token","name":"INTERNAL_TOKEN","label":"Internal JWT Token","defaultValue":"$$generate_token","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"20.0","documentation":"https://www.keycloak.org/documentation","type":"keycloak","name":"Keycloak","description":"Keycloak provides user federation, strong authentication, user management, fine-grained authorization, and more.","labels":["authentication","authorization","oidconnect","saml2"],"services":{"$$id":{"name":"Keycloak","command":"start --db=postgres --features=token-exchange --import-realm","depends_on":["$$id-postgresql"],"image":"quay.io/keycloak/keycloak:$$core_version","volumes":["$$id-import:/opt/keycloak/data/import"],"environment":["KC_HEALTH_ENABLED=true","KC_PROXY=edge","KC_DB=postgres","KC_HOSTNAME=$$config_keycloak_domain","KEYCLOAK_ADMIN=$$config_admin_user","KEYCLOAK_ADMIN_PASSWORD=$$secret_keycloak_admin_password","KC_DB_PASSWORD=$$secret_postgres_password","KC_DB_USERNAME=$$config_postgres_user","KC_DB_URL=$$secret_keycloak_database_url"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]}},"variables":[{"id":"$$config_keycloak_domain","name":"KEYCLOAK_DOMAIN","label":"Keycloak Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$secret_keycloak_database_url","name":"KEYCLOAK_DATABASE_URL","label":"Keycloak Database Url","defaultValue":"jdbc:postgresql://$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$config_admin_user","name":"KEYCLOAK_ADMIN","label":"Keycloak Admin User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_keycloak_admin_password","name":"KEYCLOAK_ADMIN_PASSWORD","label":"Keycloak Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"keycloak","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v3.7","documentation":"https://github.com/freyacodes/Lavalink","description":"Standalone audio sending node based on Lavaplayer.","type":"lavalink","name":"Lavalink","labels":["discord","discord bot","audio","lavalink","jda"],"services":{"$$id":{"name":"Lavalink","image":"fredboat/lavalink:$$core_version","environment":[],"volumes":["$$id-lavalink:/lavalink"],"ports":["2333"],"files":[{"location":"/opt/Lavalink/application.yml","content":"server:\n port: $$config_port\n address: 0.0.0.0\nlavalink:\n server:\n password: \"$$secret_password\"\n sources:\n youtube: true\n bandcamp: true\n soundcloud: true\n twitch: true\n vimeo: true\n http: true\n local: false\n\nlogging:\n file:\n path: ./logs/\n\n level:\n root: INFO\n lavalink: INFO\n\n logback:\n rollingpolicy:\n max-file-size: 1GB\n max-history: 30"}]}},"variables":[{"id":"$$config_port","name":"PORT","label":"Port","defaultValue":"2333","required":true},{"id":"$$secret_password","name":"PASSWORD","label":"Password","defaultValue":"$$generate_password","required":true}]},{"templateVersion":"1.0.0","defaultVersion":"v1.8.9","documentation":"https://docs.appsmith.com/getting-started/setup/instance-configuration/","type":"appsmith","name":"Appsmith","description":"Fastest way to build internal apps over any database or API.","services":{"$$id":{"image":"appsmith/appsmith-ce:$$core_version","environment":["APPSMITH_MAIL_ENABLED=$$config_appsmith_mail_enabled","APPSMITH_DISABLE_TELEMETRY=$$config_appsmith_disable_telemetry","APPSMITH_DISABLE_INTERCOM=$$config_appsmith_disable_intercom"],"volumes":["$$id-stacks-data:/appsmith-stacks"],"ports":["80"]}},"variables":[{"id":"$$config_appsmith_mail_enabled","name":"APPSMITH_MAIL_ENABLED","label":"Enable Mail","defaultValue":"false","description":""},{"id":"$$config_appsmith_disable_telemetry","name":"APPSMITH_DISABLE_TELEMETRY","label":"Disable Telemetry","defaultValue":"true","description":""},{"id":"$$config_appsmith_disable_intercom","name":"APPSMITH_DISABLE_INTERCOM","label":"Disable Intercom","defaultValue":"true","description":""}]},{"templateVersion":"1.0.0","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":["personal","knowledge","notes","wiki"],"type":"trilium","name":"Trilium Notes","services":{"$$id":{"image":"zadam/trilium:$$core_version","environment":[],"volumes":["$$id-trilium:/home/node/trilium-data"],"ports":["8080"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.18.5","documentation":"https://hub.docker.com/r/louislam/uptime-kuma","description":"A free & fancy self-hosted monitoring tool.","labels":["uptime"],"type":"uptimekuma","name":"UptimeKuma","services":{"$$id":{"image":"louislam/uptime-kuma:$$core_version","environment":[],"volumes":["$$id-uptimekuma:/app/data"],"ports":["3001"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"5.8","documentation":"https://hub.docker.com/r/silviof/docker-languagetool","description":"A multilingual grammar, style and spell checker.","type":"languagetool","name":"LanguageTool","services":{"$$id":{"image":"silviof/docker-languagetool:$$core_version","environment":[],"volumes":["$$id-ngrams:/ngrams"],"ports":["8010"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.26.0","documentation":"https://hub.docker.com/r/vaultwarden/server","description":"Bitwarden compatible server written in Rust.","type":"vaultwarden","name":"VaultWarden","labels":["bitwarden","password manager"],"services":{"$$id":{"image":"vaultwarden/server:$$core_version","environment":[],"volumes":["$$id-data:/data"],"ports":["80"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"9.3.1","documentation":"https://hub.docker.com/r/grafana/grafana","type":"grafana","name":"Grafana","description":"Grafana allows you to query, visualize, alert on and understand your metrics.","labels":["monitoring","metrics","dashboard"],"services":{"$$id":{"image":"grafana/grafana:$$core_version","environment":[],"volumes":["$$id-config:/etc/grafana","$$id-grafana:/var/lib/grafana"],"ports":["3000"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.1.2","documentation":"https://appwrite.io/docs","type":"appwrite","name":"Appwrite","description":"Secure Backend Server for Web, Mobile & Flutter Developers.","labels":["serverless","backend","storage","api"],"services":{"$$id":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_WORKER_PER_CORE=$$config__app_worker_per_core","_APP_LOCALE=$$config__app_locale","_APP_CONSOLE_WHITELIST_ROOT=$$config__app_console_whitelist_root","_APP_CONSOLE_WHITELIST_EMAILS=$$config__app_console_whitelist_emails","_APP_CONSOLE_WHITELIST_IPS=$$config__app_console_whitelist_ips","_APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name","_APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_SYSTEM_RESPONSE_FORMAT=$$config__app_system_response_format","_APP_OPTIONS_ABUSE=$$config__app_options_abuse","_APP_OPTIONS_FORCE_HTTPS=$$config__app_options_force_https","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_SMTP_HOST=$$config__app_smtp_host","_APP_SMTP_PORT=$$config__app_smtp_port","_APP_SMTP_SECURE=$$config__app_smtp_secure","_APP_SMTP_USERNAME=$$config__app_smtp_username","_APP_SMTP_PASSWORD=$$secret__app_smtp_password","_APP_USAGE_STATS=$$config__app_usage_stats","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_STORAGE_LIMIT=$$config__app_storage_limit","_APP_STORAGE_PREVIEW_LIMIT=$$config__app_storage_preview_limit","_APP_STORAGE_ANTIVIRUS=$$config__app_storage_antivirus_enabled","_APP_STORAGE_ANTIVIRUS_HOST=$$config__app_storage_antivirus_host","_APP_STORAGE_ANTIVIRUS_PORT=$$config__app_storage_antivirus_port","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","_APP_FUNCTIONS_SIZE_LIMIT=$$config__app_functions_size_limit","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout","_APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers","_APP_FUNCTIONS_CPUS=$$config__app_functions_cpus","_APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated","_APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap","_APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_STATSD_HOST=$$config__app_statsd_host","_APP_STATSD_PORT=$$config__app_statsd_port","_APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval","_APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution","_APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache","_APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse","_APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit","_APP_SMS_PROVIDER=$$config__app_sms_provider","_APP_SMS_FROM=$$config__app_sms_from","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-uploads:/storage/uploads","$$id-cache:/storage/cache","$$id-config:/storage/config","$$id-certificates:/storage/certificates","$$id-functions:/storage/functions"],"ports":["80"],"proxy":[{"port":"80"}]},"$$id-executor":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout","_APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers","_APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes","_APP_FUNCTIONS_CPUS=$$config__app_functions_cpus","_APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated","_APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap","_APP_FUNCTIONS_INACTIVE_THRESHOLD=$$config__app_functions_inactive_threshold","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username","DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$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","environment":[],"volumes":["$$id-influxdb:/var/lib/influxdb"]},"$$id-maintenance":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval","_APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution","_APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache","_APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse","_APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"maintenance"},"$$id-mariadb":{"image":"mariadb:10.7","command":"--innodb-flush-method fsync","environment":["MARIADB_ROOT_PASSWORD=$$secret__app_db_root_pass","MARIADB_DATABASE=$$config__app_db_schema","MARIADB_USER=$$config__app_db_user","MARIADB_PASSWORD=$$secret__app_db_pass","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-mariadb:/var/lib/mysql"]},"$$id-realtime":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_WORKER_PER_CORE=$$config__app_worker_per_core","_APP_OPTIONS_ABUSE=$$config__app_options_abuse","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_USAGE_STATS=$$config__app_usage_stats","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"realtime","proxy":[{"port":"80","pathPrefix":"/v1/realtime"}]},"$$id-redis":{"image":"redis:7.0.4-alpine","command":"--maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples 5","environment":[],"volumes":["$$id-redis:/data"]},"$$id-schedule":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"schedule"},"$$id-telegraf":{"image":"appwrite/telegraf:1.4.0","environment":["_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-influxdb:/var/lib/influxdb"]},"$$id-usage-database":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval","_APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"usage --type database"},"$$id-usage":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval","_APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"usage --type timeseries"},"$$id-worker-audits":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-audits"},"$$id-worker-builds":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-builds"},"$$id-worker-certificates":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-config:/storage/config","$$id-certificates:/storage/certificates"],"entrypoint":"worker-certificates"},"$$id-worker-databases":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-databases"},"$$id-worker-deletes":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-uploads:/storage/uploads","$$id-cache:/storage/cache","$$id-functions:/storage/functions","$$id-builds:/storage/builds","$$id-certificates:/storage/certificates"],"entrypoint":"worker-deletes"},"$$id-worker-functions":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_USAGE_STATS=$$config__app_usage_stats","DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username","DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-functions"},"$$id-worker-mails":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name","_APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_SMTP_HOST=$$config__app_smtp_host","_APP_SMTP_PORT=$$config__app_smtp_port","_APP_SMTP_SECURE=$$config__app_smtp_secure","_APP_SMTP_USERNAME=$$config__app_smtp_username","_APP_SMTP_PASSWORD=$$secret__app_smtp_password","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-mails"},"$$id-worker-messaging":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_SMS_PROVIDER=$$config__app_sms_provider","_APP_SMS_FROM=$$config__app_sms_from","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-messaging"},"$$id-worker-webhooks":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-webhooks"}},"variables":[{"id":"$$config__app_influxdb_host","name":"_APP_INFLUXDB_HOST","label":"InfluxDB | _APP_INFLUXDB_HOST","defaultValue":"$$id-influxdb","description":""},{"id":"$$config__app_influxdb_port","name":"_APP_INFLUXDB_PORT","label":"InfluxDB | _APP_INFLUXDB_PORT","defaultValue":"8086","description":"InfluxDB server TCP port."},{"id":"$$config__app_env","name":"_APP_ENV","label":"General | _APP_ENV","defaultValue":"production","description":"Set your server running environment."},{"id":"$$config__app_worker_per_core","name":"_APP_WORKER_PER_CORE","label":"General | _APP_WORKER_PER_CORE","defaultValue":"6","description":"Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance."},{"id":"$$config__app_locale","name":"_APP_LOCALE","label":"General | _APP_LOCALE","defaultValue":"en","description":"Set your Appwrite's locale. By default, the locale is set to 'en'."},{"id":"$$config__app_console_whitelist_root","name":"_APP_CONSOLE_WHITELIST_ROOT","label":"General | _APP_CONSOLE_WHITELIST_ROOT","defaultValue":"enabled","description":"This option allows you to disable the creation of new users on the Appwrite console. When enabled only 1 user will be able to use the registration form. New users can be added by inviting them to your project. By default this option is enabled."},{"id":"$$config__app_console_whitelist_emails","name":"_APP_CONSOLE_WHITELIST_EMAILS","label":"General | _APP_CONSOLE_WHITELIST_EMAILS","defaultValue":"","description":"This option allows you to limit creation of new users on the Appwrite console. This option is very useful for small teams or sole developers. To enable it, pass a list of allowed email addresses separated by a comma."},{"id":"$$config__app_console_whitelist_ips","name":"_APP_CONSOLE_WHITELIST_IPS","label":"General | _APP_CONSOLE_WHITELIST_IPS","defaultValue":"","description":"This last option allows you to limit creation of users in Appwrite console for users sharing the same set of IP addresses. This option is very useful for team working with a VPN service or a company IP.\\n\\nTo enable/activate this option, pass a list of allowed IP addresses separated by a comma."},{"id":"$$config__app_system_email_name","name":"_APP_SYSTEM_EMAIL_NAME","label":"General | _APP_SYSTEM_EMAIL_NAME","defaultValue":"Appwrite","description":"This is the sender name value that will appear on email messages sent to developers from the Appwrite console. You can use url encoded strings for spaces and special chars."},{"id":"$$config__app_system_email_address","name":"_APP_SYSTEM_EMAIL_ADDRESS","label":"General | _APP_SYSTEM_EMAIL_ADDRESS","defaultValue":"team@appwrite.io","description":"This is the sender email address that will appear on email messages sent to developers from the Appwrite console. You should choose an email address that is allowed to be used from your SMTP server to avoid the server email ending in the users' SPAM folders."},{"id":"$$config__app_system_security_email_address","name":"_APP_SYSTEM_SECURITY_EMAIL_ADDRESS","label":"General | _APP_SYSTEM_SECURITY_EMAIL_ADDRESS","defaultValue":"certs@appwrite.io","description":"This is the email address used to issue SSL certificates for custom domains or the user agent in your webhooks payload."},{"id":"$$config__app_system_response_format","name":"_APP_SYSTEM_RESPONSE_FORMAT","label":"General | _APP_SYSTEM_RESPONSE_FORMAT","defaultValue":"","description":"Use this environment variable to set the default Appwrite HTTP response format to support an older version of Appwrite. This option is useful to overcome breaking changes between versions. You can also use the X-Appwrite-Response-Format HTTP request header to overwrite the response for a specific request. This variable accepts any valid Appwrite version. To use the current version format, leave the value of the variable empty."},{"id":"$$config__app_options_abuse","name":"_APP_OPTIONS_ABUSE","label":"General | _APP_OPTIONS_ABUSE","defaultValue":"enabled","description":"Allows you to disable abuse checks and API rate limiting. By default, set to 'enabled'. To cancel the abuse checking, set to 'disabled'. It is not recommended to disable this check-in a production environment."},{"id":"$$config__app_options_force_https","name":"_APP_OPTIONS_FORCE_HTTPS","label":"General | _APP_OPTIONS_FORCE_HTTPS","defaultValue":"disabled","description":"Allows you to force HTTPS connection to your API. This feature redirects any HTTP call to HTTPS and adds the 'Strict-Transport-Security' header to all HTTP responses."},{"id":"$$secret__app_openssl_key_v1","name":"_APP_OPENSSL_KEY_V1","label":"General | _APP_OPENSSL_KEY_V1","defaultValue":"$$generate_hex(256)","description":"This is your server private secret key that is used to encrypt all sensitive data on your server. Appwrite server encrypts all secret data on your server like webhooks, HTTP passwords, user sessions, and storage files. Keep it a secret and have a backup for it."},{"id":"$$config__app_domain","name":"_APP_DOMAIN","label":"General | _APP_DOMAIN","defaultValue":"$$generate_domain","description":"Your Appwrite domain address. When setting a public suffix domain, Appwrite will attempt to issue a valid SSL certificate automatically. When used with a dev domain, Appwrite will assign a self-signed SSL certificate. The default value is 'localhost'."},{"id":"$$config__app_domain_target","name":"_APP_DOMAIN_TARGET","label":"General | _APP_DOMAIN_TARGET","defaultValue":"$$generate_fqdn","description":"A DNS A record hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite '_APP_DOMAIN' variable. The default value is 'localhost'."},{"id":"$$config__app_redis_host","name":"_APP_REDIS_HOST","label":"Redis | _APP_REDIS_HOST","defaultValue":"$$id-redis","description":""},{"id":"$$config__app_redis_port","name":"_APP_REDIS_PORT","label":"Redis | _APP_REDIS_PORT","defaultValue":"6379","description":"Redis server TCP port."},{"id":"$$config__app_redis_user","name":"_APP_REDIS_USER","label":"Redis | _APP_REDIS_USER","defaultValue":"","description":"Redis server user. This is an optional variable. Default value is an empty string."},{"id":"$$secret__app_redis_pass","name":"_APP_REDIS_PASS","label":"Redis | _APP_REDIS_PASS","defaultValue":"","description":"Redis server password. This is an optional variable. Default value is an empty string."},{"id":"$$config__app_db_host","name":"_APP_DB_HOST","label":"MariaDB | _APP_DB_HOST","defaultValue":"$$id-mariadb","description":""},{"id":"$$config__app_db_port","name":"_APP_DB_PORT","label":"MariaDB | _APP_DB_PORT","defaultValue":"3306","description":"MariaDB server TCP port."},{"id":"$$config__app_db_schema","name":"_APP_DB_SCHEMA","label":"MariaDB | _APP_DB_SCHEMA","defaultValue":"appwrite","description":"MariaDB server database schema."},{"id":"$$config__app_db_user","name":"_APP_DB_USER","label":"MariaDB | _APP_DB_USER","defaultValue":"user","description":"MariaDB server user name."},{"id":"$$secret__app_db_root_pass","name":"MARIADB_ROOT_PASSWORD","label":"MariaDB | MARIADB_ROOT_PASSWORD","defaultValue":"$$generate_hex(16)","description":"MariaDB server root user password."},{"id":"$$secret__app_db_pass","name":"_APP_DB_PASS","label":"MariaDB | _APP_DB_PASS","defaultValue":"$$generate_hex(16)","description":"MariaDB server user password."},{"id":"$$config__app_smtp_host","name":"_APP_SMTP_HOST","label":"SMTP | _APP_SMTP_HOST","defaultValue":"","description":"SMTP server host name address. Use an empty string to disable all mail sending from the server. The default value for this variable is an empty string."},{"id":"$$config__app_smtp_port","name":"_APP_SMTP_PORT","label":"SMTP | _APP_SMTP_PORT","defaultValue":"","description":"SMTP server TCP port. Empty by default."},{"id":"$$config__app_smtp_secure","name":"_APP_SMTP_SECURE","label":"SMTP | _APP_SMTP_SECURE","defaultValue":"","description":"SMTP secure connection protocol. Empty by default, change to 'tls' if running on a secure connection."},{"id":"$$config__app_smtp_username","name":"_APP_SMTP_USERNAME","label":"SMTP | _APP_SMTP_USERNAME","defaultValue":"","description":"SMTP server user name. Empty by default."},{"id":"$$secret__app_smtp_password","name":"_APP_SMTP_PASSWORD","label":"SMTP | _APP_SMTP_PASSWORD","defaultValue":"","description":"SMTP server user password. Empty by default."},{"id":"$$config__app_usage_stats","name":"_APP_USAGE_STATS","label":"General | _APP_USAGE_STATS","defaultValue":"enabled","description":"This variable allows you to disable the collection and displaying of usage stats. This value is set to 'enabled' by default, to disable the usage stats set the value to 'disabled'. When disabled, it's recommended to turn off the Worker Usage, Influxdb and Telegraf containers for better resource usage."},{"id":"$$config__app_storage_limit","name":"_APP_STORAGE_LIMIT","label":"Storage | _APP_STORAGE_LIMIT","defaultValue":"30000000","description":"Maximum file size allowed for file upload. The default value is 30MB. You should pass your size limit value in bytes."},{"id":"$$config__app_storage_preview_limit","name":"_APP_STORAGE_PREVIEW_LIMIT","label":"Storage | _APP_STORAGE_PREVIEW_LIMIT","defaultValue":"20000000","description":"Maximum file size allowed for file image preview. The default value is 20MB. You should pass your size limit value in bytes."},{"id":"$$config__app_storage_antivirus_enabled","name":"_APP_STORAGE_ANTIVIRUS","label":"Storage | _APP_STORAGE_ANTIVIRUS","defaultValue":"disabled","description":"This variable allows you to disable the internal anti-virus scans. This value is set to 'disabled' by default, to enable the scans set the value to 'enabled'. Before enabling, you must add the ClamAV service and depend on it on main Appwrite service."},{"id":"$$config__app_storage_antivirus_host","name":"_APP_STORAGE_ANTIVIRUS_HOST","label":"Storage | _APP_STORAGE_ANTIVIRUS_HOST","defaultValue":"clamav","description":"ClamAV server host name address."},{"id":"$$config__app_storage_antivirus_port","name":"_APP_STORAGE_ANTIVIRUS_PORT","label":"Storage | _APP_STORAGE_ANTIVIRUS_PORT","defaultValue":"3310","description":"ClamAV server TCP port."},{"id":"$$config__app_storage_device","name":"_APP_STORAGE_DEVICE","label":"Storage | _APP_STORAGE_DEVICE","defaultValue":"Local","description":"Select default storage device. The default value is 'Local'. List of supported adapters are 'Local', 'S3', 'DOSpaces', 'Backblaze', 'Linode' and 'Wasabi'."},{"id":"$$secret__app_storage_s3_access_key","name":"_APP_STORAGE_S3_ACCESS_KEY","label":"Storage | _APP_STORAGE_S3_ACCESS_KEY","defaultValue":"","description":"AWS S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your AWS console."},{"id":"$$secret__app_storage_s3_secret","name":"_APP_STORAGE_S3_SECRET","label":"Storage | _APP_STORAGE_S3_SECRET","defaultValue":"","description":"AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console."},{"id":"$$config__app_storage_s3_region","name":"_APP_STORAGE_S3_REGION","label":"Storage | _APP_STORAGE_S3_REGION","defaultValue":"us-east-1","description":"AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console."},{"id":"$$config__app_storage_s3_bucket","name":"_APP_STORAGE_S3_BUCKET","label":"Storage | _APP_STORAGE_S3_BUCKET","defaultValue":"","description":"AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console."},{"id":"$$secret__app_storage_do_spaces_access_key","name":"_APP_STORAGE_DO_SPACES_ACCESS_KEY","label":"Storage | _APP_STORAGE_DO_SPACES_ACCESS_KEY","defaultValue":"","description":"DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console."},{"id":"$$secret__app_storage_do_spaces_secret","name":"_APP_STORAGE_DO_SPACES_SECRET","label":"Storage | _APP_STORAGE_DO_SPACES_SECRET","defaultValue":"","description":"DigitalOcean spaces secret key. Required when the storage adapter is set to DOSpaces. You can get your secret key from your DigitalOcean console."},{"id":"$$config__app_storage_do_spaces_region","name":"_APP_STORAGE_DO_SPACES_REGION","label":"Storage | _APP_STORAGE_DO_SPACES_REGION","defaultValue":"us-east-1","description":"DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console."},{"id":"$$config__app_storage_do_spaces_bucket","name":"_APP_STORAGE_DO_SPACES_BUCKET","label":"Storage | _APP_STORAGE_DO_SPACES_BUCKET","defaultValue":"","description":"DigitalOcean spaces bucket. Required when storage adapter is set to DOSpaces. You can create spaces in your DigitalOcean console."},{"id":"$$secret__app_storage_backblaze_access_key","name":"_APP_STORAGE_BACKBLAZE_ACCESS_KEY","label":"Storage | _APP_STORAGE_BACKBLAZE_ACCESS_KEY","defaultValue":"","description":"Backblaze access key. Required when the storage adapter is set to Backblaze. Your Backblaze keyID will be your access key. You can get your keyID from your Backblaze console."},{"id":"$$secret__app_storage_backblaze_secret","name":"_APP_STORAGE_BACKBLAZE_SECRET","label":"Storage | _APP_STORAGE_BACKBLAZE_SECRET","defaultValue":"","description":"Backblaze secret key. Required when the storage adapter is set to Backblaze. Your Backblaze applicationKey will be your secret key. You can get your applicationKey from your Backblaze console."},{"id":"$$config__app_storage_backblaze_region","name":"_APP_STORAGE_BACKBLAZE_REGION","label":"Storage | _APP_STORAGE_BACKBLAZE_REGION","defaultValue":"us-west-004","description":"Backblaze region. Required when storage adapter is set to Backblaze. You can find your region info from your Backblaze console."},{"id":"$$config__app_storage_backblaze_bucket","name":"_APP_STORAGE_BACKBLAZE_BUCKET","label":"Storage | _APP_STORAGE_BACKBLAZE_BUCKET","defaultValue":"","description":"Backblaze bucket. Required when storage adapter is set to Backblaze. You can create your bucket from your Backblaze console."},{"id":"$$secret__app_storage_linode_access_key","name":"_APP_STORAGE_LINODE_ACCESS_KEY","label":"Storage | _APP_STORAGE_LINODE_ACCESS_KEY","defaultValue":"","description":"Linode object storage access key. Required when the storage adapter is set to Linode. You can get your access key from your Linode console."},{"id":"$$secret__app_storage_linode_secret","name":"_APP_STORAGE_LINODE_SECRET","label":"Storage | _APP_STORAGE_LINODE_SECRET","defaultValue":"","description":"Linode object storage secret key. Required when the storage adapter is set to Linode. You can get your secret key from your Linode console."},{"id":"$$config__app_storage_linode_region","name":"_APP_STORAGE_LINODE_REGION","label":"Storage | _APP_STORAGE_LINODE_REGION","defaultValue":"eu-central-1","description":"Linode object storage region. Required when storage adapter is set to Linode. You can find your region info from your Linode console."},{"id":"$$config__app_storage_linode_bucket","name":"_APP_STORAGE_LINODE_BUCKET","label":"Storage | _APP_STORAGE_LINODE_BUCKET","defaultValue":"","description":"Linode object storage bucket. Required when storage adapter is set to Linode. You can create buckets in your Linode console."},{"id":"$$secret__app_storage_wasabi_access_key","name":"_APP_STORAGE_WASABI_ACCESS_KEY","label":"Storage | _APP_STORAGE_WASABI_ACCESS_KEY","defaultValue":"","description":"Wasabi access key. Required when the storage adapter is set to Wasabi. You can get your access key from your Wasabi console."},{"id":"$$secret__app_storage_wasabi_secret","name":"_APP_STORAGE_WASABI_SECRET","label":"Storage | _APP_STORAGE_WASABI_SECRET","defaultValue":"","description":"Wasabi secret key. Required when the storage adapter is set to Wasabi. You can get your secret key from your Wasabi console."},{"id":"$$config__app_storage_wasabi_region","name":"_APP_STORAGE_WASABI_REGION","label":"Storage | _APP_STORAGE_WASABI_REGION","defaultValue":"eu-central-1","description":"Wasabi region. Required when storage adapter is set to Wasabi. You can find your region info from your Wasabi console."},{"id":"$$config__app_storage_wasabi_bucket","name":"_APP_STORAGE_WASABI_BUCKET","label":"Storage | _APP_STORAGE_WASABI_BUCKET","defaultValue":"","description":"Wasabi bucket. Required when storage adapter is set to Wasabi. You can create buckets in your Wasabi console."},{"id":"$$config__app_functions_size_limit","name":"_APP_FUNCTIONS_SIZE_LIMIT","label":"Functions | _APP_FUNCTIONS_SIZE_LIMIT","defaultValue":"30000000","description":"The maximum size deployment in bytes. The default value is 30MB."},{"id":"$$config__app_functions_timeout","name":"_APP_FUNCTIONS_TIMEOUT","label":"Functions | _APP_FUNCTIONS_TIMEOUT","defaultValue":"900","description":"The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds."},{"id":"$$config__app_functions_build_timeout","name":"_APP_FUNCTIONS_BUILD_TIMEOUT","label":"Functions | _APP_FUNCTIONS_BUILD_TIMEOUT","defaultValue":"900","description":"The maximum number of seconds allowed as a timeout value when building a new function. The default value is 900 seconds."},{"id":"$$config__app_functions_containers","name":"_APP_FUNCTIONS_CONTAINERS","label":"Functions | _APP_FUNCTIONS_CONTAINERS","defaultValue":"10","description":"The maximum number of containers Appwrite is allowed to keep alive in the background for function environments. Running containers allow faster execution time as there is no need to recreate each container every time a function gets executed. The default value is 10."},{"id":"$$config__app_functions_cpus","name":"_APP_FUNCTIONS_CPUS","label":"Functions | _APP_FUNCTIONS_CPUS","defaultValue":"","description":"The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it's empty, CPU limit will be disabled."},{"id":"$$config__app_functions_memory_allocated","name":"_APP_FUNCTIONS_MEMORY","label":"Functions | _APP_FUNCTIONS_MEMORY","defaultValue":"","description":"The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it's empty, memory limit will be disabled."},{"id":"$$config__app_functions_memory_swap","name":"_APP_FUNCTIONS_MEMORY_SWAP","label":"Functions | _APP_FUNCTIONS_MEMORY_SWAP","defaultValue":"","description":"The maximum amount of swap memory a single cloud function is allowed to use in megabytes. The default value is empty. When it's empty, swap memory limit will be disabled."},{"id":"$$config__app_functions_runtimes","name":"_APP_FUNCTIONS_RUNTIMES","label":"Functions | _APP_FUNCTIONS_RUNTIMES","defaultValue":"node-18.0","description":"This option allows you to limit the available environments for cloud functions. This option is very useful for low-cost servers to safe disk space.\nTo enable/activate this option, pass a list of allowed environments separated by a comma.\nCurrently, supported environments are: node-14.5, node-16.0, node-18.0, php-8.0, php-8.1, ruby-3.0, ruby-3.1, python-3.8, python-3.9, python-3.10, deno-1.21, deno-1.24, dart-2.15, dart-2.16, dart-2.17, dotnet-3.1, dotnet-6.0, java-8.0, java-11.0, java-17.0, java-18.0, swift-5.5, kotlin-1.6, cpp-17.0"},{"id":"$$secret__app_executor_secret","name":"_APP_EXECUTOR_SECRET","label":"Functions | _APP_EXECUTOR_SECRET","defaultValue":"$$generate_hex(16)","description":"The secret key used by Appwrite to communicate with the function executor."},{"id":"$$config__app_executor_host","name":"_APP_EXECUTOR_HOST","label":"","defaultValue":"http://$$id-executor/v1","description":""},{"id":"$$config__app_logging_provider","name":"_APP_LOGGING_PROVIDER","label":"General | _APP_LOGGING_PROVIDER","defaultValue":"","description":"This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, to enable the logger set the value to one of 'sentry', 'raygun', 'appsignal', 'logowl'"},{"id":"$$config__app_logging_config","name":"_APP_LOGGING_CONFIG","label":"General | _APP_LOGGING_CONFIG","defaultValue":"","description":"This variable configures authentication to 3rd party error logging providers. If using Sentry, this should be 'SENTRY_API_KEY;SENTRY_APP_ID'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket."},{"id":"$$config__app_statsd_host","name":"_APP_STATSD_HOST","label":"","defaultValue":"$$id-telegraf","description":""},{"id":"$$config__app_statsd_port","name":"_APP_STATSD_PORT","label":"StatsD | _APP_STATSD_PORT","defaultValue":"8125","description":"StatsD server TCP port."},{"id":"$$config__app_maintenance_interval","name":"_APP_MAINTENANCE_INTERVAL","label":"Functions | _APP_MAINTENANCE_INTERVAL","defaultValue":"86400","description":"Interval value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 86400 seconds (1 day)."},{"id":"$$config__app_maintenance_retention_execution","name":"_APP_MAINTENANCE_RETENTION_EXECUTION","label":"Functions | _APP_MAINTENANCE_RETENTION_EXECUTION","defaultValue":"1209600","description":"The maximum duration (in seconds) upto which to retain execution logs. The default value is 1209600 seconds (14 days)."},{"id":"$$config__app_maintenance_retention_cache","name":"_APP_MAINTENANCE_RETENTION_CACHE","label":"Functions | _APP_MAINTENANCE_RETENTION_CACHE","defaultValue":"2592000","description":"The maximum duration (in seconds) upto which to retain cached files. The default value is 2592000 seconds (30 days)."},{"id":"$$config__app_maintenance_retention_abuse","name":"_APP_MAINTENANCE_RETENTION_ABUSE","label":"Functions | _APP_MAINTENANCE_RETENTION_ABUSE","defaultValue":"86400","description":"The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day)."},{"id":"$$config__app_maintenance_retention_audit","name":"_APP_MAINTENANCE_RETENTION_AUDIT","label":"Functions | _APP_MAINTENANCE_RETENTION_AUDIT","defaultValue":"1209600","description":"The maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days)."},{"id":"$$config__app_sms_provider","name":"_APP_SMS_PROVIDER","label":"Phone | _APP_SMS_PROVIDER","defaultValue":"","description":"Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'. Available providers are twilio, text-magic, telesign, msg91, and vonage."},{"id":"$$config__app_sms_from","name":"_APP_SMS_FROM","label":"Phone | _APP_SMS_FROM","defaultValue":"","description":"Phone number used for sending out messages. Must start with a leading '+' and maximum of 15 digits without spaces (+123456789)."},{"id":"$$config__app_functions_inactive_threshold","name":"_APP_FUNCTIONS_INACTIVE_THRESHOLD","label":"Functions | _APP_FUNCTIONS_INACTIVE_THRESHOLD","defaultValue":"60","description":"The minimum time a function can be inactive before it's container is shutdown and put to sleep. The default value is 60 seconds"},{"id":"$$config_open_runtimes_network","name":"OPEN_RUNTIMES_NETWORK","label":"","defaultValue":"$$generate_network","description":""},{"id":"$$config_dockerhub_pull_username","name":"DOCKERHUB_PULL_USERNAME","label":"Functions | DOCKERHUB_PULL_USERNAME","defaultValue":"","description":"The username for hub.docker.com. This variable is used to pull images from hub.docker.com."},{"id":"$$secret_dockerhub_pull_password","name":"DOCKERHUB_PULL_PASSWORD","label":"Functions | DOCKERHUB_PULL_PASSWORD","defaultValue":"","description":"The password for hub.docker.com. This variable is used to pull images from hub.docker.com."},{"id":"$$config__app_usage_timeseries_interval","name":"_APP_USAGE_TIMESERIES_INTERVAL","label":"General | _APP_USAGE_TIMESERIES_INTERVAL","defaultValue":"30","description":"Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats and syncing it to mariadb from InfluxDB. The default value is 30 seconds."},{"id":"$$config__app_usage_database_interval","name":"_APP_USAGE_DATABASE_INTERVAL","label":"General | _APP_USAGE_DATABASE_INTERVAL","defaultValue":"900","description":"Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats from data in Appwrite Database. The default value is 15 minutes."}]},{"templateVersion":"1.0.0","defaultVersion":"latest","documentation":"https://docs.weblate.org/en/latest/admin/install/docker.html","description":"A copylefted libre software web-based continuous localization system.","type":"weblate","name":"Weblate","labels":["translate","localization"],"services":{"$$id":{"name":"Weblate","depends_on":["$$id-postgresql","$$id-redis"],"image":"weblate/weblate:$$core_version","volumes":["$$id-data:/app/data"],"environment":["WEBLATE_SITE_DOMAIN=$$config_weblate_site_domain","WEBLATE_ADMIN_PASSWORD=$$secret_weblate_admin_password","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_USER=$$config_postgres_user","POSTGRES_DATABASE=$$config_postgres_db","POSTGRES_HOST=$$id-postgresql","POSTGRES_PORT=5432","REDIS_HOST=$$id-redis"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]},"$$id-redis":{"name":"Redis","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-redis-data:/data"],"environment":[],"ports":[]}},"variables":[{"id":"$$config_weblate_site_domain","name":"WEBLATE_SITE_DOMAIN","label":"Weblate Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$secret_weblate_admin_password","name":"WEBLATE_ADMIN_PASSWORD","label":"Weblate Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"weblate","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"2022.12.12-966e9c3c","documentation":"https://docs.searxng.org/","type":"searxng","name":"SearXNG","description":"Free internet metasearch engine which aggregates results from more than 70 search services.","services":{"$$id":{"name":"SearXNG","depends_on":["$$id-redis"],"image":"searxng/searxng:$$core_version","volumes":["$$id-searxng:/etc/searxng"],"environment":["SEARXNG_BASE_URL=$$config_searxng_base_url"],"ports":["8080"],"cap_drop":["ALL"],"cap_add":["CHOWN","SETGID","SETUID","DAC_OVERRIDE"],"files":[{"location":"/etc/searxng/settings.yml","content":"\n # see https://docs.searxng.org/admin/engines/settings.html#use-default-settings\n use_default_settings: true\n server:\n secret_key: $$secret_secret_key\n limiter: true\n image_proxy: true\n ui:\n static_use_hash: true\n redis:\n url: redis://:$$secret_redis_password@$$id-redis:6379/0"}]},"$$id-redis":{"name":"Redis","command":"redis-server --requirepass $$secret_redis_password --save \"\" --appendonly \"no\"","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-redis-data:/data"],"environment":["REDIS_PASSWORD=$$secret_redis_password"],"ports":[],"cap_drop":["ALL"],"cap_add":["SETGID","SETUID","DAC_OVERRIDE"]}},"variables":[{"id":"$$config_searxng_base_url","name":"SEARXNG_BASE_URL","label":"SearXNG Base URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_secret_key","name":"SECRET_KEY","label":"Secret Key","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$secret_redis_password","name":"REDIS_PASSWORD","label":"Redis Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v3.0.0","documentation":"https://glitchtip.com/documentation","type":"glitchtip","name":"GlitchTip","description":"Simple, open source error tracking.","labels":["sentry","bugsnag"],"services":{"$$id":{"name":"GlitchTip","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","volumes":[],"environment":["PORT=$$config_port","GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url","EMAIL_HOST=$$config_email_host","EMAIL_PORT=$$config_email_port","EMAIL_HOST_USER=$$config_email_host_user","EMAIL_HOST_PASSWORD=$$secret_email_host_password","EMAIL_USE_TLS=$$config_email_use_tls","EMAIL_USE_SSL=$$config_email_use_ssl","EMAIL_BACKEND=$$config_email_backend","MAILGUN_API_KEY=$$secret_mailgun_api_key","SENDGRID_API_KEY=$$secret_sendgrid_api_key","ENABLE_OPEN_USER_REGISTRATION=$$config_enable_open_user_registration","DJANGO_SUPERUSER_EMAIL=$$config_django_superuser_email","DJANGO_SUPERUSER_PASSWORD=$$secret_django_superuser_password","DJANGO_SUPERUSER_USERNAME=$$config_django_superuser_username","CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency"],"ports":["8000"]},"$$id-worker":{"name":"Celery Worker","command":"./bin/run-celery-with-beat.sh","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","environment":["GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url","CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency"],"ports":[]},"$$id-migrate":{"exclude":true,"name":"Migrate","command":"./manage.py migrate","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","environment":["GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url"],"ports":[]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]},"$$id-redis":{"name":"Redis","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-postgresql-redis-data:/data"],"environment":[],"ports":[]}},"variables":[{"id":"$$config_django_superuser_username","name":"DJANGO_SUPERUSER_USERNAME","label":"Django Superuser Username","defaultValue":"$$generate_username","description":""},{"id":"$$secret_django_superuser_password","name":"DJANGO_SUPERUSER_PASSWORD","label":"Django Superuser Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_port","name":"PORT","label":"GlitchTip Port","defaultValue":"8000","description":""},{"id":"$$config_celery_worker_concurrency","main":"$$id-worker","name":"CELERY_WORKER_CONCURRENCY","label":"Celery Worker Concurrency","defaultValue":"2","description":""},{"id":"$$config_glitchtip_domain","name":"GLITCHTIP_DOMAIN","label":"GlitchTip Domain","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_email_url","name":"EMAIL_URL","label":"SMTP Email URL","defaultValue":"smtp://$$config_email_host_user:$$secret_email_host_password@$$config_email_host:$$config_email_port","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_redis_url","name":"REDIS_URL","label":"Redis URL","defaultValue":"redis://$$id-redis:6379/0","description":""},{"id":"$$config_default_from_email","name":"DEFAULT_FROM_EMAIL","label":"Default Email Address","defaultValue":"noreply@example.com","description":""},{"id":"$$config_email_host","name":"EMAIL_HOST","label":"Email SMTP Host","defaultValue":"","description":""},{"id":"$$config_email_port","name":"EMAIL_PORT","label":"Email SMTP Port","defaultValue":"25","description":""},{"id":"$$config_email_host_user","name":"EMAIL_HOST_USER","label":"Email SMTP User","defaultValue":"","description":""},{"id":"$$secret_email_host_password","name":"EMAIL_HOST_PASSWORD","label":"Email SMTP Password","defaultValue":"","description":""},{"id":"$$config_email_use_tls","name":"EMAIL_USE_TLS","label":"Email Use TLS","defaultValue":"false","description":""},{"id":"$$config_email_use_ssl","name":"EMAIL_USE_SSL","label":"Email Use SSL","defaultValue":"false","description":""},{"id":"$$secret_email_smtp_password","name":"EMAIL_SMTP_PASSWORD","label":"SMTP Password","defaultValue":"","description":""},{"id":"$$config_email_backend","name":"EMAIL_BACKEND","label":"Email Backend","defaultValue":"","description":""},{"id":"$$secret_mailgun_api_key","name":"MAILGUN_API_KEY","label":"Mailgun API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$secret_sendgrid_api_key","name":"SENDGRID_API_KEY","label":"Sendgrid API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_enable_open_user_registration","name":"ENABLE_OPEN_USER_REGISTRATION","label":"Enable Open User Registration","defaultValue":"true","description":""},{"id":"$$config_django_superuser_email","name":"DJANGO_SUPERUSER_EMAIL","label":"Django Superuser Email","defaultValue":"noreply@example.com","description":""},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"glitchtip","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v2.16.0","documentation":"https://hasura.io/docs/latest/index/","type":"hasura","name":"Hasura","description":"Instant realtime GraphQL APIs on any Postgres application, existing or new.","labels":["graphql","database"],"services":{"$$id":{"name":"Hasura","depends_on":["$$id-postgresql"],"image":"hasura/graphql-engine:$$core_version","volumes":[],"environment":["HASURA_GRAPHQL_ENABLE_CONSOLE=$$config_hasura_graphql_enable_console","HASURA_GRAPHQL_METADATA_DATABASE_URL=$$secret_hasura_graphql_metadata_database_url","HASURA_GRAPHQL_ADMIN_SECRET=$$secret_hasura_graphql_admin_secret"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]}},"variables":[{"id":"$$config_hasura_graphql_enable_console","name":"HASURA_GRAPHQL_ENABLE_CONSOLE","label":"Enable Hasura Console","defaultValue":"true","description":""},{"id":"$$secret_hasura_graphql_metadata_database_url","name":"HASURA_GRAPHQL_METADATA_DATABASE_URL","label":"Hasura Metadata Database URL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hasura_graphql_admin_secret","name":"HASURA_GRAPHQL_ADMIN_SECRET","label":"Hasura Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"hasura","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"postgresql-v1.39.5","documentation":"https://umami.is/docs/getting-started","type":"umami-postgresql","name":"Umami","subname":"(PostgreSQL)","description":"A simple, easy to use, self-hosted web analytics solution.","services":{"$$id":{"name":"Umami","depends_on":["$$id-postgresql"],"image":"ghcr.io/umami-software/umami:$$core_version","volumes":[],"environment":["ADMIN_PASSWORD=$$secret_admin_password","DATABASE_URL=$$secret_database_url","DATABASE_TYPE=$$config_database_type","HASH_SALT=$$secret_hash_salt"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[],"files":[{"location":"/docker-entrypoint-initdb.d/schema.postgresql.sql","content":"\n -- CreateTable\n CREATE TABLE \"account\" (\n \"user_id\" SERIAL NOT NULL,\n \"username\" VARCHAR(255) NOT NULL,\n \"password\" VARCHAR(60) NOT NULL,\n \"is_admin\" BOOLEAN NOT NULL DEFAULT false,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"updated_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \n PRIMARY KEY (\"user_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"event\" (\n \"event_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"event_type\" VARCHAR(50) NOT NULL,\n \"event_value\" VARCHAR(50) NOT NULL,\n \n PRIMARY KEY (\"event_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"pageview\" (\n \"view_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"referrer\" VARCHAR(500),\n \n PRIMARY KEY (\"view_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"session\" (\n \"session_id\" SERIAL NOT NULL,\n \"session_uuid\" UUID NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"hostname\" VARCHAR(100),\n \"browser\" VARCHAR(20),\n \"os\" VARCHAR(20),\n \"device\" VARCHAR(20),\n \"screen\" VARCHAR(11),\n \"language\" VARCHAR(35),\n \"country\" CHAR(2),\n \n PRIMARY KEY (\"session_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"website\" (\n \"website_id\" SERIAL NOT NULL,\n \"website_uuid\" UUID NOT NULL,\n \"user_id\" INTEGER NOT NULL,\n \"name\" VARCHAR(100) NOT NULL,\n \"domain\" VARCHAR(500),\n \"share_id\" VARCHAR(64),\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \n PRIMARY KEY (\"website_id\")\n );\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"account.username_unique\" ON \"account\"(\"username\");\n \n -- CreateIndex\n CREATE INDEX \"event_created_at_idx\" ON \"event\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"event_session_id_idx\" ON \"event\"(\"session_id\");\n \n -- CreateIndex\n CREATE INDEX \"event_website_id_idx\" ON \"event\"(\"website_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_created_at_idx\" ON \"pageview\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_session_id_idx\" ON \"pageview\"(\"session_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_idx\" ON \"pageview\"(\"website_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_session_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"session_id\", \"created_at\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"session.session_uuid_unique\" ON \"session\"(\"session_uuid\");\n \n -- CreateIndex\n CREATE INDEX \"session_created_at_idx\" ON \"session\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"session_website_id_idx\" ON \"session\"(\"website_id\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"website.website_uuid_unique\" ON \"website\"(\"website_uuid\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"website.share_id_unique\" ON \"website\"(\"share_id\");\n \n -- CreateIndex\n CREATE INDEX \"website_user_id_idx\" ON \"website\"(\"user_id\");\n \n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"session\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"website\" ADD FOREIGN KEY (\"user_id\") REFERENCES \"account\"(\"user_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);"}]}},"variables":[{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hash_salt","name":"HASH_SALT","label":"Hash Salt","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_database_type","name":"DATABASE_TYPE","label":"Database Type","defaultValue":"postgresql","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"umami","description":""},{"id":"$$secret_admin_password","name":"ADMIN_PASSWORD","label":"Initial Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","ignore":true,"defaultVersion":"postgresql-v1.39.5","documentation":"https://umami.is/docs/getting-started","type":"umami","name":"Umami","subname":"(PostgreSQL)","description":"A simple, easy to use, self-hosted web analytics solution.","services":{"$$id":{"name":"Umami","depends_on":["$$id-postgresql"],"image":"ghcr.io/umami-software/umami:$$core_version","volumes":[],"environment":["ADMIN_PASSWORD=$$secret_admin_password","DATABASE_URL=$$secret_database_url","DATABASE_TYPE=$$config_database_type","HASH_SALT=$$secret_hash_salt"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[],"files":[{"location":"/docker-entrypoint-initdb.d/schema.postgresql.sql","content":"\n -- CreateTable\n CREATE TABLE \"account\" (\n \"user_id\" SERIAL NOT NULL,\n \"username\" VARCHAR(255) NOT NULL,\n \"password\" VARCHAR(60) NOT NULL,\n \"is_admin\" BOOLEAN NOT NULL DEFAULT false,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"updated_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \n PRIMARY KEY (\"user_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"event\" (\n \"event_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"event_type\" VARCHAR(50) NOT NULL,\n \"event_value\" VARCHAR(50) NOT NULL,\n \n PRIMARY KEY (\"event_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"pageview\" (\n \"view_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"referrer\" VARCHAR(500),\n \n PRIMARY KEY (\"view_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"session\" (\n \"session_id\" SERIAL NOT NULL,\n \"session_uuid\" UUID NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"hostname\" VARCHAR(100),\n \"browser\" VARCHAR(20),\n \"os\" VARCHAR(20),\n \"device\" VARCHAR(20),\n \"screen\" VARCHAR(11),\n \"language\" VARCHAR(35),\n \"country\" CHAR(2),\n \n PRIMARY KEY (\"session_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"website\" (\n \"website_id\" SERIAL NOT NULL,\n \"website_uuid\" UUID NOT NULL,\n \"user_id\" INTEGER NOT NULL,\n \"name\" VARCHAR(100) NOT NULL,\n \"domain\" VARCHAR(500),\n \"share_id\" VARCHAR(64),\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \n PRIMARY KEY (\"website_id\")\n );\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"account.username_unique\" ON \"account\"(\"username\");\n \n -- CreateIndex\n CREATE INDEX \"event_created_at_idx\" ON \"event\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"event_session_id_idx\" ON \"event\"(\"session_id\");\n \n -- CreateIndex\n CREATE INDEX \"event_website_id_idx\" ON \"event\"(\"website_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_created_at_idx\" ON \"pageview\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_session_id_idx\" ON \"pageview\"(\"session_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_idx\" ON \"pageview\"(\"website_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_session_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"session_id\", \"created_at\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"session.session_uuid_unique\" ON \"session\"(\"session_uuid\");\n \n -- CreateIndex\n CREATE INDEX \"session_created_at_idx\" ON \"session\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"session_website_id_idx\" ON \"session\"(\"website_id\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"website.website_uuid_unique\" ON \"website\"(\"website_uuid\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"website.share_id_unique\" ON \"website\"(\"share_id\");\n \n -- CreateIndex\n CREATE INDEX \"website_user_id_idx\" ON \"website\"(\"user_id\");\n \n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"session\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"website\" ADD FOREIGN KEY (\"user_id\") REFERENCES \"account\"(\"user_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);"}]}},"variables":[{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hash_salt","name":"HASH_SALT","label":"Hash Salt","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_database_type","name":"DATABASE_TYPE","label":"Database Type","defaultValue":"postgresql","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"umami","description":""},{"id":"$$secret_admin_password","name":"ADMIN_PASSWORD","label":"Initial Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"v0.30.1","documentation":"https://docs.meilisearch.com/learn/getting_started/quick_start.html","type":"meilisearch","name":"MeiliSearch","description":"A lightning Fast, Ultra Relevant, and Typo-Tolerant Search Engine.","services":{"$$id":{"name":"MeiliSearch","documentation":"https://docs.meilisearch.com/","depends_on":[],"image":"getmeili/meilisearch:$$core_version","volumes":["$$id-datams:/meili_data/data.ms","$$id-data:/meili_data","$$id-snapshot:/snapshot","$$id-dump:/dumps"],"environment":["MEILI_MASTER_KEY=$$secret_meili_master_key"],"ports":["7700"]}},"variables":[{"id":"$$secret_meili_master_key","name":"MEILI_MASTER_KEY","label":"Master Key","defaultValue":"$$generate_hex(64)","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","ignore":true,"defaultVersion":"latest","documentation":"https://docs.ghost.org","arch":"amd64","type":"ghost-mariadb","name":"Ghost","subname":"(MariaDB)","description":"Free and open source blogging platform.","labels":["cms","blog"],"services":{"$$id":{"name":"Ghost","depends_on":["$$id-mariadb"],"image":"bitnami/ghost:$$core_version","volumes":["$$id-ghost:/bitnami/ghost"],"environment":["url=$$config_url","GHOST_HOST=$$config_ghost_host","GHOST_ENABLE_HTTPS=$$config_ghost_enable_https","GHOST_EMAIL=$$config_ghost_email","GHOST_PASSWORD=$$secret_ghost_password","GHOST_DATABASE_HOST=$$config_ghost_database_host","GHOST_DATABASE_USER=$$config_mariadb_user","GHOST_DATABASE_PASSWORD=$$secret_ghost_database_password","GHOST_DATABASE_NAME=$$config_mariadb_database","GHOST_DATABASE_PORT_NUMBER=3306"],"ports":["2368"]},"$$id-mariadb":{"name":"MariaDB","depends_on":[],"image":"bitnami/mariadb:latest","volumes":["$$id-mariadb:/bitnami/mariadb"],"environment":["MARIADB_USER=$$config_mariadb_user","MARIADB_PASSWORD=$$secret_mariadb_password","MARIADB_DATABASE=$$config_mariadb_database","MARIADB_ROOT_USER=$$config_mariadb_root_user","MARIADB_ROOT_PASSWORD=$$secret_mariadb_root_password"],"ports":[]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_ghost_host","name":"GHOST_HOST","label":"Ghost Host","defaultValue":"$$generate_domain","description":""},{"id":"$$config_ghost_enable_https","name":"GHOST_ENABLE_HTTPS","label":"Ghost Enable HTTPS","defaultValue":"no","description":""},{"id":"$$config_ghost_email","name":"GHOST_EMAIL","label":"Ghost Default Email","defaultValue":"admin@example.com","description":""},{"id":"$$secret_ghost_password","name":"GHOST_PASSWORD","label":"Ghost Default Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_ghost_database_host","name":"GHOST_DATABASE_HOST","label":"Ghost Database Host","defaultValue":"$$id-mariadb","description":""},{"id":"$$config_ghost_database_user","name":"GHOST_DATABASE_USER","label":"MariaDB User","defaultValue":"$$config_mariadb_user","description":""},{"id":"$$secret_ghost_database_password","name":"GHOST_DATABASE_PASSWORD","label":"MariaDB Password","defaultValue":"$$secret_mariadb_password","description":""},{"id":"$$config_ghost_database_name","name":"GHOST_DATABASE_NAME","label":"MariaDB Database","defaultValue":"$$config_mariadb_database","description":""},{"id":"$$config_mariadb_user","name":"MARIADB_USER","label":"MariaDB User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mariadb_password","name":"MARIADB_PASSWORD","label":"MariaDB Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_mariadb_database","name":"MARIADB_DATABASE","label":"MariaDB Database","defaultValue":"ghost","description":""},{"id":"$$config_mariadb_root_user","name":"MARIADB_ROOT_USER","label":"MariaDB Root User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mariadb_root_password","name":"MARIADB_ROOT_PASSWORD","label":"MariaDB Root Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"5.25.3","documentation":"https://docs.ghost.org","type":"ghost-only","name":"Ghost","subname":"(without Database)","description":"Free and open source blogging platform.","services":{"$$id":{"name":"Ghost","image":"ghost:$$core_version","volumes":["$$id-ghost:/var/lib/ghost/content"],"environment":["url=$$config_url","database__client=$$config_database__client","database__connection__host=$$config_database__connection__host","database__connection__user=$$config_database__connection__user","database__connection__password=$$secret_database__connection__password","database__connection__database=$$config_database__connection__database"],"ports":["2368"]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_database__client","name":"database__client","label":"Database Client","defaultValue":"mysql","description":"","required":true},{"id":"$$config_database__connection__host","name":"database__connection__host","label":"Database Host","defaultValue":"","description":"","required":true,"placeholder":"db.coolify.io"},{"id":"$$config_database__connection__user","name":"database__connection__user","label":"Database User","defaultValue":"","description":"","placeholder":"ghost","required":true},{"id":"$$secret_database__connection__password","name":"database__connection__password","label":"Database Password","defaultValue":"","description":"","placeholder":"superSecretP4ssword","showOnConfiguration":true,"required":true},{"id":"$$config_database__connection__database","name":"database__connection__database","label":"Database Name","defaultValue":"","description":"","placeholder":"ghost_db","required":true}]},{"templateVersion":"1.0.0","defaultVersion":"5.25.3","documentation":"https://docs.ghost.org","type":"ghost-mysql","name":"Ghost","subname":"(MySQL)","description":"Ghost is a free and open source blogging platform.","services":{"$$id":{"name":"Ghost","depends_on":["$$id-mysql"],"image":"ghost:$$core_version","volumes":["$$id-ghost:/var/lib/ghost/content"],"environment":["url=$$config_url","database__client=$$config_database__client","database__connection__host=$$config_database__connection__host","database__connection__user=$$config_mysql_user","database__connection__password=$$secret_mysql_password","database__connection__database=$$config_mysql_database"],"ports":["2368"]},"$$id-mysql":{"name":"MySQL","depends_on":[],"image":"mysql:8.0","volumes":["$$id-mysql:/var/lib/mysql"],"environment":["MYSQL_USER=$$config_mysql_user","MYSQL_PASSWORD=$$secret_mysql_password","MYSQL_DATABASE=$$config_mysql_database","MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password"],"ports":[]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_database__client","name":"database__client","label":"Database Client","defaultValue":"mysql","description":"","readOnly":true},{"id":"$$config_database__connection__host","name":"database__connection__host","label":"Database Host","defaultValue":"$$id-mysql","description":""},{"id":"$$config_mysql_user","main":"$$id-mysql","name":"MYSQL_USER","label":"MySQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mysql_password","main":"$$id-mysql","name":"MYSQL_PASSWORD","label":"MySQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_mysql_database","main":"$$id-mysql","name":"MYSQL_DATABASE","label":"MySQL Database","defaultValue":"ghost","description":""},{"id":"$$secret_mysql_root_password","name":"MYSQL_ROOT_PASSWORD","label":"MySQL Root Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"php8.1","documentation":"https://wordpress.org/","type":"wordpress","name":"WordPress","subname":"(MySQL)","description":"A content management system based on PHP.","labels":["wordpress","php","cms"],"services":{"$$id":{"name":"WordPress","depends_on":["$$id-mysql"],"image":"wordpress:$$core_version","volumes":["$$id-wordpress-data:/var/www/html"],"environment":["WORDPRESS_DB_HOST=$$config_wordpress_db_host","WORDPRESS_DB_USER=$$config_mysql_user","WORDPRESS_DB_PASSWORD=$$secret_mysql_password","WORDPRESS_DB_NAME=$$config_mysql_database","WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra"],"ports":["80"]},"$$id-mysql":{"name":"MySQL","depends_on":[],"image":"bitnami/mysql:5.7","imageArm":"mysql:8.0","volumes":["$$id-mysql-data:/bitnami/mysql/data"],"volumesArm":["$$id-mysql-data:/var/lib/mysql"],"environment":["MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password","MYSQL_ROOT_USER=$$config_mysql_root_user","MYSQL_DATABASE=$$config_mysql_database","MYSQL_USER=$$config_mysql_user","MYSQL_PASSWORD=$$secret_mysql_password"]}},"variables":[{"id":"$$config_wordpress_db_host","name":"WORDPRESS_DB_HOST","label":"Database Host","defaultValue":"$$id-mysql","description":"","readOnly":true},{"id":"$$config_wordpress_config_extra","name":"WORDPRESS_CONFIG_EXTRA","label":"WordPress Config Extra","defaultValue":"","description":"","type":"textarea","placeholder":"define('WP_DEBUG', true);\ndefine('WP_DEBUG_LOG', true);\ndefine('WP_DEBUG_DISPLAY', false);\n@ini_set('display_errors', 0);\n"},{"id":"$$secret_mysql_root_password","name":"MYSQL_ROOT_PASSWORD","label":"MySQL Root Password","defaultValue":"$$generate_password","description":"","readOnly":true},{"id":"$$config_mysql_root_user","name":"MYSQL_ROOT_USER","label":"MySQL Root User","defaultValue":"$$generate_username","description":"","readOnly":true},{"id":"$$config_mysql_database","name":"MYSQL_DATABASE","label":"MySQL Database","defaultValue":"wordpress","description":"","readOnly":true},{"id":"$$config_mysql_user","name":"MYSQL_USER","label":"MySQL User","defaultValue":"$$generate_username","description":"","readOnly":true},{"id":"$$secret_mysql_password","name":"MYSQL_PASSWORD","label":"MySQL Password","defaultValue":"$$generate_password","description":"","readOnly":true}]},{"templateVersion":"1.0.0","defaultVersion":"php8.1","documentation":"https://wordpress.org/","type":"wordpress-only","name":"WordPress","subname":"(without DB)","description":"A content management system based on PHP.","labels":["wordpress","php","cms"],"services":{"$$id":{"name":"WordPress","image":"wordpress:$$core_version","volumes":["$$id-wordpress-data:/var/www/html"],"environment":["WORDPRESS_DB_HOST=$$config_wordpress_db_host","WORDPRESS_DB_PORT=$$config_wordpress_db_port","WORDPRESS_DB_USER=$$config_wordpress_db_user","WORDPRESS_DB_PASSWORD=$$secret_wordpress_db_password","WORDPRESS_DB_NAME=$$config_wordpress_db_name","WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra"],"ports":["80"]}},"variables":[{"id":"$$config_wordpress_db_host","name":"WORDPRESS_DB_HOST","label":"Database Host","defaultValue":"","description":"","placeholder":"db.coollabs.io","required":true},{"id":"$$config_wordpress_db_port","name":"WORDPRESS_DB_PORT","label":"Database Port","defaultValue":"","description":"","placeholder":"3306","required":true},{"id":"$$config_wordpress_db_user","name":"WORDPRESS_DB_USER","label":"Database User","defaultValue":"","description":"","placeholder":"wordpress","required":true},{"id":"$$secret_wordpress_db_password","name":"WORDPRESS_DB_PASSWORD","label":"Database Password","defaultValue":"","description":"","placeholder":"supers3cr3tpassw0rd!","required":true,"showOnConfiguration":true},{"id":"$$config_wordpress_db_name","name":"WORDPRESS_DB_NAME","label":"Database Name","defaultValue":"","description":"","placeholder":"wordpress","required":true},{"id":"$$config_wordpress_config_extra","name":"WORDPRESS_CONFIG_EXTRA","label":"Extra Config","defaultValue":"","description":"","type":"textarea","placeholder":"define('WP_DEBUG', true);\ndefine('WP_DEBUG_LOG', true);\ndefine('WP_DEBUG_DISPLAY', false);\n@ini_set('display_errors', 0);\n"}]},{"templateVersion":"1.0.0","defaultVersion":"4.9.0","documentation":"https://coder.com/docs/coder-oss/latest","type":"vscodeserver","name":"VSCode Server","description":"Visual Studio Code on a remote server, accessible through the browser.","labels":["vscode","ide"],"services":{"$$id":{"name":"VSCode Server","depends_on":[],"image":"codercom/code-server:$$core_version","volumes":["$$id-config-data:/home/coder/.local/share/code-server","$$id-vscodeserver-data:/home/coder","$$id-keys-directory:/root/.ssh","$$id-theme-and-plugin-directory:/root/.local/share/code-server"],"environment":["PASSWORD=$$secret_password"],"ports":["8080"]}},"variables":[{"id":"$$secret_password","name":"PASSWORD","label":"Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"RELEASE.2022-12-12T19-27-27Z","documentation":"https://min.io/docs/minio","type":"minio","name":"MinIO","description":"A cloud storage server compatible with Amazon S3.","labels":["storage","s3"],"services":{"$$id":{"name":"MinIO","command":"server /data --console-address :9001","depends_on":[],"image":"minio/minio:$$core_version","volumes":["$$id-minio-data:/data","$$id-data-write:/files"],"environment":["MINIO_SERVER_URL=$$config_coolify_fqdn_minio_console","MINIO_BROWSER_REDIRECT_URL=$$config_minio_browser_redirect_url","MINIO_DOMAIN=$$config_minio_domain","MINIO_ROOT_USER=$$config_minio_root_user","MINIO_ROOT_PASSWORD=$$secret_minio_root_password"],"ports":["9000","9001"],"proxy":[{"port":"9000","domain":"$$config_coolify_fqdn_minio_console"},{"port":"9001"}]}},"variables":[{"id":"$$config_coolify_fqdn_minio_console","name":"MINIO_SERVER_URL","label":"MinIO Server URL","defaultValue":"","description":"Specify the URL hostname the MinIO Console should use for connecting to the MinIO Server.","required":true},{"id":"$$config_minio_browser_redirect_url","name":"MINIO_BROWSER_REDIRECT_URL","label":"Browser Redirect URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_minio_domain","name":"MINIO_DOMAIN","label":"Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_minio_root_user","name":"MINIO_ROOT_USER","label":"Root User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_minio_root_password","name":"MINIO_ROOT_PASSWORD","label":"Root User Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"0.21.1","documentation":"https://fider.io/docs","type":"fider","name":"Fider","description":"A platform to collect and organize customer feedback.","labels":["suggestion","feedback"],"services":{"$$id":{"name":"Fider","image":"getfider/fider:$$core_version","depends_on":["$$id-postgresql"],"environment":["BASE_URL=$$config_base_url","DATABASE_URL=$$secret_database_url","JWT_SECRET=$$secret_jwt_secret","EMAIL_NOREPLY=$$config_email_noreply","EMAIL_MAILGUN_API=$$secret_email_mailgun_api","EMAIL_MAILGUN_REGION=$$config_email_mailgun_region","EMAIL_MAILGUN_DOMAIN=$$config_email_mailgun_domain","EMAIL_SMTP_HOST=$$config_email_smtp_host","EMAIL_SMTP_PORT=$$config_email_smtp_port","EMAIL_SMTP_USER=$$config_email_smtp_user","EMAIL_SMTP_PASSWORD=$$secret_email_smtp_password","EMAIL_SMTP_ENABLE_STARTTLS=$$config_email_smtp_enable_starttls"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db?sslmode=disable","description":""},{"id":"$$secret_jwt_secret","name":"JWT_SECRET","label":"JWT Secret","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_email_noreply","name":"EMAIL_NOREPLY","label":"No Reply Email Address","defaultValue":"noreply@example.com","description":""},{"id":"$$secret_email_mailgun_api","name":"EMAIL_MAILGUN_API","label":"Mailgun API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_email_mailgun_region","name":"EMAIL_MAILGUN_REGION","label":"Mailgun Region","defaultValue":"EU","description":""},{"id":"$$config_email_mailgun_domain","name":"EMAIL_MAILGUN_DOMAIN","label":"Mailgun Domain","defaultValue":"","description":""},{"id":"$$config_email_smtp_host","name":"EMAIL_SMTP_HOST","label":"SMTP Host","defaultValue":"","description":""},{"id":"$$config_email_smtp_port","name":"EMAIL_SMTP_PORT","label":"SMTP Port","defaultValue":"587","description":""},{"id":"$$config_email_smtp_user","name":"EMAIL_SMTP_USER","label":"SMTP User","defaultValue":"","description":""},{"id":"$$secret_email_smtp_password","name":"EMAIL_SMTP_PASSWORD","label":"SMTP Password","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_email_smtp_enable_starttls","name":"EMAIL_SMTP_ENABLE_STARTTLS","label":"SMTP Enable StartTLS","defaultValue":"false","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"$$generate_username","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"0.207.0","documentation":"https://docs.n8n.io","type":"n8n","name":"n8n.io","description":"A free and open node based Workflow Automation Tool.","labels":["workflow","automation","ifttt","zapier","nodered"],"services":{"$$id":{"name":"N8n","depends_on":[],"image":"n8nio/n8n:$$core_version","volumes":["$$id-data:/root/.n8n","$$id-data-write:/files","/var/run/docker.sock:/var/run/docker.sock"],"environment":["WEBHOOK_URL=$$config_webhook_url"],"ports":["5678"]}},"variables":[{"id":"$$config_webhook_url","name":"WEBHOOK_URL","label":"Webhook URL","defaultValue":"$$generate_fqdn","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"stable","documentation":"https://plausible.io/doc/","arch":"amd64","type":"plausibleanalytics","name":"Plausible Analytics","description":"A lightweight and open-source website analytics tool.","labels":["analytics","statistics","plausible","gdpr","no-cookie","google analytics"],"services":{"$$id":{"name":"Plausible Analytics","command":"sh -c \"sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run\"","depends_on":["$$id-postgresql","$$id-clickhouse"],"image":"plausible/analytics:$$core_version","environment":["ADMIN_USER_EMAIL=$$config_admin_user_email","ADMIN_USER_NAME=$$config_admin_user_name","ADMIN_USER_PWD=$$secret_admin_user_pwd","BASE_URL=$$config_base_url","SECRET_KEY_BASE=$$secret_secret_key_base","DISABLE_AUTH=$$config_disable_auth","DISABLE_REGISTRATION=$$config_disable_registration","DATABASE_URL=$$secret_database_url","CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url"],"ports":["8000"]},"$$id-postgresql":{"name":"PostgreSQL","image":"bitnami/postgresql:13","volumes":["$$id-postgresql-data:/bitnami/postgresql"],"environment":["POSTGRESQL_PASSWORD=$$secret_postgresql_password","POSTGRESQL_USERNAME=$$config_postgresql_username","POSTGRESQL_DATABASE=$$config_postgresql_database"]},"$$id-clickhouse":{"name":"Clickhouse","volumes":["$$id-clickhouse-data:/var/lib/clickhouse"],"image":"clickhouse/clickhouse-server:22.6-alpine","ulimits":{"nofile":{"soft":262144,"hard":262144}},"files":[{"location":"/etc/clickhouse-server/users.d/logging.xml","content":"warningtrue"},{"location":"/etc/clickhouse-server/config.d/logging.xml","content":"00"},{"location":"/docker-entrypoint-initdb.d/init.query","content":"CREATE DATABASE IF NOT EXISTS plausible;"},{"location":"/docker-entrypoint-initdb.d/init-db.sh","content":"clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query"}]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":"You must set this to the FQDN of the Plausible Analytics instance. This is used to generate the links to the Plausible Analytics instance."},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgresql_username:$$secret_postgresql_password@$$id-postgresql:5432/$$config_postgresql_database","description":""},{"id":"$$secret_clickhouse_database_url","name":"CLICKHOUSE_DATABASE_URL","label":"Database URL for Clickhouse","defaultValue":"http://$$id-clickhouse:8123/plausible","description":""},{"id":"$$config_admin_user_email","name":"ADMIN_USER_EMAIL","label":"Admin Email Address","defaultValue":"admin@example.com","description":"This is the admin email. Please change it."},{"id":"$$config_admin_user_name","name":"ADMIN_USER_NAME","label":"Admin User Name","defaultValue":"$$generate_username","description":"This is the admin username. Please change it."},{"id":"$$secret_admin_user_pwd","name":"ADMIN_USER_PWD","label":"Admin User Password","defaultValue":"$$generate_password","description":"This is the admin password. Please change it.","showOnConfiguration":true},{"id":"$$secret_secret_key_base","name":"SECRET_KEY_BASE","label":"Secret Key Base","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_disable_auth","name":"DISABLE_AUTH","label":"Disable Authentication","defaultValue":"false","description":""},{"id":"$$config_disable_registration","name":"DISABLE_REGISTRATION","label":"Disable Registration","defaultValue":"true","description":""},{"id":"$$config_postgresql_username","main":"$$id-postgresql","name":"POSTGRESQL_USERNAME","label":"PostgreSQL Username","defaultValue":"postgresql","description":""},{"id":"$$secret_postgresql_password","main":"$$id-postgresql","name":"POSTGRESQL_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgresql_database","main":"$$id-postgresql","name":"POSTGRESQL_DATABASE","label":"PostgreSQL Database","defaultValue":"plausible","description":""},{"id":"$$config_scriptName","name":"SCRIPT_NAME","label":"Custom Script Name","defaultValue":"plausible.js","description":"This is the default script name."}]},{"templateVersion":"1.0.0","defaultVersion":"0.99.1","documentation":"https://docs.nocodb.com","type":"nocodb","name":"NocoDB","description":"Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadsheet.","labels":["database","airtable","spreadsheet"],"services":{"$$id":{"name":"NocoDB","image":"nocodb/nocodb:$$core_version","environment":["PORT=$$config_port","NC_DB=$$config_nc_db","DATABASE_URL=$$secret_database_url","NC_PUBLIC_URL=$$config_public_url","NC_AUTH_JWT_SECRET=$$secret_auth_jwt_secret","NC_SENTRY_DSN=$$secret_sentry_dsn","NC_CONNECT_TO_EXTERNAL_DB_DISABLED=$$config_connect_to_external_db_disabled","NC_DISABLE_TELE=$$config_disable_tele"],"volumes":["$$id-data:/usr/app/data"],"ports":["8080"]}},"variables":[{"id":"$$config_nc_db","name":"NC_DB","label":"Database","defaultValue":"","description":"MySQL, PostgreSQL and MSSQL connection urls supported. If absent: A local SQLite will be created in root folder."},{"id":"$$config_port","name":"PORT","label":"Port","defaultValue":"8080","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL","defaultValue":"","description":"JDBC URL Format. Can be used instead of NC_DB. Used in 1-Click Heroku deployment."},{"id":"$$config_public_url","name":"NC_PUBLIC_URL","label":"Public URL","defaultValue":"","description":"Used for sending Email invitations. If absent: Best guess from http request params."},{"id":"$$secret_auth_jwt_secret","name":"NC_AUTH_JWT_SECRET","label":"Auth JWT Secret","defaultValue":"$$generate_hex(64)","description":"JWT secret used for auth and storing other secrets. If absent: A Random secret will be generated."},{"id":"$$secret_sentry_dsn","name":"NC_SENTRY_DSN","label":"Sentry DSN","defaultValue":"","description":"For Sentry monitoring."},{"id":"$$config_connect_to_external_db_disabled","name":"NC_CONNECT_TO_EXTERNAL_DB_DISABLED","label":"Disable External Database","defaultValue":"0","description":"Disable Project creation with external database. (Enter \"1\" to disable)."},{"id":"$$config_disable_tele","name":"NC_DISABLE_TELE","label":"NocoDB Disable Telemetry","defaultValue":"1","description":"Disable telemetry (Enter \"1\" to disable)."}]}] \ No newline at end of file +[{"templateVersion":"1.0.0","defaultVersion":"0.8.0","documentation":"https://pocketbase.io/docs/","type":"pocketbase","name":"Pocketbase","description":"Open Source realtime backend in 1 file","services":{"$$id":{"image":"coollabsio/pocketbase:$$core_version","volumes":["$$id-data:/app/pb_data"],"ports":["8080"]}}},{"templateVersion":"1.0.0","defaultVersion":"v1.5.1","documentation":"https://plausible.io/doc/","type":"plausibleanalytics-arm","name":"Plausible Analytics (ARM)","description":"A lightweight and open-source website analytics tool.","labels":["analytics","statistics","plausible","gdpr","no-cookie","google analytics"],"services":{"$$id":{"name":"Plausible Analytics","command":"sh -c \"sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run\"","depends_on":["$$id-postgresql","$$id-clickhouse"],"image":"plausible/analytics:$$core_version","environment":["ADMIN_USER_EMAIL=$$config_admin_user_email","ADMIN_USER_NAME=$$config_admin_user_name","ADMIN_USER_PWD=$$secret_admin_user_pwd","BASE_URL=$$config_base_url","SECRET_KEY_BASE=$$secret_secret_key_base","DISABLE_AUTH=$$config_disable_auth","DISABLE_REGISTRATION=$$config_disable_registration","DATABASE_URL=$$secret_database_url","CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url"],"ports":["8000"]},"$$id-postgresql":{"name":"PostgreSQL","image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_USER=$$config_postgres_user","POSTGRES_DB=$$config_postgres_db"]},"$$id-clickhouse":{"name":"Clickhouse","volumes":["$$id-clickhouse-data:/var/lib/clickhouse"],"image":"clickhouse/clickhouse-server:22.6-alpine","ulimits":{"nofile":{"soft":262144,"hard":262144}},"files":[{"location":"/etc/clickhouse-server/users.d/logging.xml","content":"warningtrue"},{"location":"/etc/clickhouse-server/config.d/logging.xml","content":"00"},{"location":"/docker-entrypoint-initdb.d/init.query","content":"CREATE DATABASE IF NOT EXISTS plausible;"},{"location":"/docker-entrypoint-initdb.d/init-db.sh","content":"clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query"}]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":"You must set this to the FQDN of the Plausible Analytics instance. This is used to generate the links to the Plausible Analytics instance."},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_clickhouse_database_url","name":"CLICKHOUSE_DATABASE_URL","label":"Database URL for Clickhouse","defaultValue":"http://$$id-clickhouse:8123/plausible","description":""},{"id":"$$config_admin_user_email","name":"ADMIN_USER_EMAIL","label":"Admin Email Address","defaultValue":"admin@example.com","description":"This is the admin email. Please change it."},{"id":"$$config_admin_user_name","name":"ADMIN_USER_NAME","label":"Admin User Name","defaultValue":"$$generate_username","description":"This is the admin username. Please change it."},{"id":"$$secret_admin_user_pwd","name":"ADMIN_USER_PWD","label":"Admin User Password","defaultValue":"$$generate_password","description":"This is the admin password. Please change it.","showOnConfiguration":true},{"id":"$$secret_secret_key_base","name":"SECRET_KEY_BASE","label":"Secret Key Base","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_disable_auth","name":"DISABLE_AUTH","label":"Disable Authentication","defaultValue":"false","description":""},{"id":"$$config_disable_registration","name":"DISABLE_REGISTRATION","label":"Disable Registration","defaultValue":"true","description":""},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL Username","defaultValue":"postgresql","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"plausible","description":""},{"id":"$$config_scriptName","name":"SCRIPT_NAME","label":"Custom Script Name","defaultValue":"plausible.js","description":"This is the default script name."}]},{"templateVersion":"1.0.0","defaultVersion":"1.17","documentation":"https://docs.gitea.io","type":"gitea","name":"Gitea","description":"Gitea is a community managed lightweight code hosting solution written in Go.","labels":["storage","git"],"services":{"$$id":{"name":"Gitea","documentation":"https://docs.gitea.io","image":"gitea/gitea:$$core_version","volumes":["$$id-data:/data","/etc/timezone:/etc/timezone:ro","/etc/localtime:/etc/localtime:ro"],"environment":["USER_UID=1000","USER_GID=1000","DOMAIN=$$config_domain","SSH_DOMAIN=$$config_ssh_domain","ROOT_URL=$$config_root_url","SECRET_KEY=$$secret_secret_key","INTERNAL_TOKEN=$$secret_internal_token","SSH_PORT=22","START_SSH_SERVER=$$config_start_ssh_server"],"ports":["3000","22"],"proxy":[{"port":"22","hostPort":"$$config_hostport_ssh"}]}},"variables":[{"id":"$$config_hostport_ssh","name":"SSH_PORT","label":"SSH Port","defaultValue":"8022","description":"","required":true},{"id":"$$config_domain","name":"DOMAIN","label":"Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_ssh_domain","name":"SSH_DOMAIN","label":"SSH Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_start_ssh_server","name":"START_SSH_SERVER","label":"Start SSH Server","defaultValue":"true","description":""},{"id":"$$config_root_url","name":"ROOT_URL","label":"Root URL of Gitea","defaultValue":"$$generate_fqdn_slash","description":""},{"id":"$$secret_secret_key","name":"SECRET_KEY","label":"Secret Key","defaultValue":"$$generate_hex(32)","description":""},{"id":"$$secret_internal_token","name":"INTERNAL_TOKEN","label":"Internal JWT Token","defaultValue":"$$generate_token","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"20.0","documentation":"https://www.keycloak.org/documentation","type":"keycloak","name":"Keycloak","description":"Keycloak provides user federation, strong authentication, user management, fine-grained authorization, and more.","labels":["authentication","authorization","oidconnect","saml2"],"services":{"$$id":{"name":"Keycloak","command":"start --db=postgres --features=token-exchange --import-realm","depends_on":["$$id-postgresql"],"image":"quay.io/keycloak/keycloak:$$core_version","volumes":["$$id-import:/opt/keycloak/data/import"],"environment":["KC_HEALTH_ENABLED=true","KC_PROXY=edge","KC_DB=postgres","KC_HOSTNAME=$$config_keycloak_domain","KEYCLOAK_ADMIN=$$config_admin_user","KEYCLOAK_ADMIN_PASSWORD=$$secret_keycloak_admin_password","KC_DB_PASSWORD=$$secret_postgres_password","KC_DB_USERNAME=$$config_postgres_user","KC_DB_URL=$$secret_keycloak_database_url"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]}},"variables":[{"id":"$$config_keycloak_domain","name":"KEYCLOAK_DOMAIN","label":"Keycloak Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$secret_keycloak_database_url","name":"KEYCLOAK_DATABASE_URL","label":"Keycloak Database Url","defaultValue":"jdbc:postgresql://$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$config_admin_user","name":"KEYCLOAK_ADMIN","label":"Keycloak Admin User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_keycloak_admin_password","name":"KEYCLOAK_ADMIN_PASSWORD","label":"Keycloak Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"keycloak","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v3.7","documentation":"https://github.com/freyacodes/Lavalink","description":"Standalone audio sending node based on Lavaplayer.","type":"lavalink","name":"Lavalink","labels":["discord","discord bot","audio","lavalink","jda"],"services":{"$$id":{"name":"Lavalink","image":"fredboat/lavalink:$$core_version","environment":[],"volumes":["$$id-lavalink:/lavalink"],"ports":["2333"],"files":[{"location":"/opt/Lavalink/application.yml","content":"server:\n port: $$config_port\n address: 0.0.0.0\nlavalink:\n server:\n password: \"$$secret_password\"\n sources:\n youtube: true\n bandcamp: true\n soundcloud: true\n twitch: true\n vimeo: true\n http: true\n local: false\n\nlogging:\n file:\n path: ./logs/\n\n level:\n root: INFO\n lavalink: INFO\n\n logback:\n rollingpolicy:\n max-file-size: 1GB\n max-history: 30"}]}},"variables":[{"id":"$$config_port","name":"PORT","label":"Port","defaultValue":"2333","required":true},{"id":"$$secret_password","name":"PASSWORD","label":"Password","defaultValue":"$$generate_password","required":true}]},{"templateVersion":"1.0.0","defaultVersion":"v1.8.9","documentation":"https://docs.appsmith.com/getting-started/setup/instance-configuration/","type":"appsmith","name":"Appsmith","description":"Fastest way to build internal apps over any database or API.","services":{"$$id":{"image":"appsmith/appsmith-ce:$$core_version","environment":["APPSMITH_MAIL_ENABLED=$$config_appsmith_mail_enabled","APPSMITH_DISABLE_TELEMETRY=$$config_appsmith_disable_telemetry","APPSMITH_DISABLE_INTERCOM=$$config_appsmith_disable_intercom"],"volumes":["$$id-stacks-data:/appsmith-stacks"],"ports":["80"]}},"variables":[{"id":"$$config_appsmith_mail_enabled","name":"APPSMITH_MAIL_ENABLED","label":"Enable Mail","defaultValue":"false","description":""},{"id":"$$config_appsmith_disable_telemetry","name":"APPSMITH_DISABLE_TELEMETRY","label":"Disable Telemetry","defaultValue":"true","description":""},{"id":"$$config_appsmith_disable_intercom","name":"APPSMITH_DISABLE_INTERCOM","label":"Disable Intercom","defaultValue":"true","description":""}]},{"templateVersion":"1.0.0","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":["personal","knowledge","notes","wiki"],"type":"trilium","name":"Trilium Notes","services":{"$$id":{"image":"zadam/trilium:$$core_version","environment":[],"volumes":["$$id-trilium:/home/node/trilium-data"],"ports":["8080"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.18.5","documentation":"https://hub.docker.com/r/louislam/uptime-kuma","description":"A free & fancy self-hosted monitoring tool.","labels":["uptime"],"type":"uptimekuma","name":"UptimeKuma","services":{"$$id":{"image":"louislam/uptime-kuma:$$core_version","environment":[],"volumes":["$$id-uptimekuma:/app/data"],"ports":["3001"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"5.8","documentation":"https://hub.docker.com/r/silviof/docker-languagetool","description":"A multilingual grammar, style and spell checker.","type":"languagetool","name":"LanguageTool","services":{"$$id":{"image":"silviof/docker-languagetool:$$core_version","environment":[],"volumes":["$$id-ngrams:/ngrams"],"ports":["8010"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.26.0","documentation":"https://hub.docker.com/r/vaultwarden/server","description":"Bitwarden compatible server written in Rust.","type":"vaultwarden","name":"VaultWarden","labels":["bitwarden","password manager"],"services":{"$$id":{"image":"vaultwarden/server:$$core_version","environment":[],"volumes":["$$id-data:/data"],"ports":["80"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"9.3.1","documentation":"https://hub.docker.com/r/grafana/grafana","type":"grafana","name":"Grafana","description":"Grafana allows you to query, visualize, alert on and understand your metrics.","labels":["monitoring","metrics","dashboard"],"services":{"$$id":{"image":"grafana/grafana:$$core_version","environment":[],"volumes":["$$id-config:/etc/grafana","$$id-grafana:/var/lib/grafana"],"ports":["3000"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.1.2","documentation":"https://appwrite.io/docs","type":"appwrite","name":"Appwrite","description":"Secure Backend Server for Web, Mobile & Flutter Developers.","labels":["serverless","backend","storage","api"],"services":{"$$id":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_WORKER_PER_CORE=$$config__app_worker_per_core","_APP_LOCALE=$$config__app_locale","_APP_CONSOLE_WHITELIST_ROOT=$$config__app_console_whitelist_root","_APP_CONSOLE_WHITELIST_EMAILS=$$config__app_console_whitelist_emails","_APP_CONSOLE_WHITELIST_IPS=$$config__app_console_whitelist_ips","_APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name","_APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_SYSTEM_RESPONSE_FORMAT=$$config__app_system_response_format","_APP_OPTIONS_ABUSE=$$config__app_options_abuse","_APP_OPTIONS_FORCE_HTTPS=$$config__app_options_force_https","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_SMTP_HOST=$$config__app_smtp_host","_APP_SMTP_PORT=$$config__app_smtp_port","_APP_SMTP_SECURE=$$config__app_smtp_secure","_APP_SMTP_USERNAME=$$config__app_smtp_username","_APP_SMTP_PASSWORD=$$secret__app_smtp_password","_APP_USAGE_STATS=$$config__app_usage_stats","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_STORAGE_LIMIT=$$config__app_storage_limit","_APP_STORAGE_PREVIEW_LIMIT=$$config__app_storage_preview_limit","_APP_STORAGE_ANTIVIRUS=$$config__app_storage_antivirus_enabled","_APP_STORAGE_ANTIVIRUS_HOST=$$config__app_storage_antivirus_host","_APP_STORAGE_ANTIVIRUS_PORT=$$config__app_storage_antivirus_port","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","_APP_FUNCTIONS_SIZE_LIMIT=$$config__app_functions_size_limit","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout","_APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers","_APP_FUNCTIONS_CPUS=$$config__app_functions_cpus","_APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated","_APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap","_APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_STATSD_HOST=$$config__app_statsd_host","_APP_STATSD_PORT=$$config__app_statsd_port","_APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval","_APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution","_APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache","_APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse","_APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit","_APP_SMS_PROVIDER=$$config__app_sms_provider","_APP_SMS_FROM=$$config__app_sms_from","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-uploads:/storage/uploads","$$id-cache:/storage/cache","$$id-config:/storage/config","$$id-certificates:/storage/certificates","$$id-functions:/storage/functions"],"ports":["80"],"proxy":[{"port":"80"}]},"$$id-executor":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout","_APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers","_APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes","_APP_FUNCTIONS_CPUS=$$config__app_functions_cpus","_APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated","_APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap","_APP_FUNCTIONS_INACTIVE_THRESHOLD=$$config__app_functions_inactive_threshold","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username","DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$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","environment":[],"volumes":["$$id-influxdb:/var/lib/influxdb"]},"$$id-maintenance":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval","_APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution","_APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache","_APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse","_APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"maintenance"},"$$id-mariadb":{"image":"mariadb:10.7","command":"--innodb-flush-method fsync","environment":["MARIADB_ROOT_PASSWORD=$$secret__app_db_root_pass","MARIADB_DATABASE=$$config__app_db_schema","MARIADB_USER=$$config__app_db_user","MARIADB_PASSWORD=$$secret__app_db_pass","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-mariadb:/var/lib/mysql"]},"$$id-realtime":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_WORKER_PER_CORE=$$config__app_worker_per_core","_APP_OPTIONS_ABUSE=$$config__app_options_abuse","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_USAGE_STATS=$$config__app_usage_stats","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"realtime","proxy":[{"port":"80","pathPrefix":"/v1/realtime"}]},"$$id-redis":{"image":"redis:7.0.4-alpine","command":"--maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples 5","environment":[],"volumes":["$$id-redis:/data"]},"$$id-schedule":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"schedule"},"$$id-telegraf":{"image":"appwrite/telegraf:1.4.0","environment":["_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-influxdb:/var/lib/influxdb"]},"$$id-usage-database":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval","_APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"usage --type database"},"$$id-usage":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval","_APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"usage --type timeseries"},"$$id-worker-audits":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-audits"},"$$id-worker-builds":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-builds"},"$$id-worker-certificates":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-config:/storage/config","$$id-certificates:/storage/certificates"],"entrypoint":"worker-certificates"},"$$id-worker-databases":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-databases"},"$$id-worker-deletes":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-uploads:/storage/uploads","$$id-cache:/storage/cache","$$id-functions:/storage/functions","$$id-builds:/storage/builds","$$id-certificates:/storage/certificates"],"entrypoint":"worker-deletes"},"$$id-worker-functions":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_USAGE_STATS=$$config__app_usage_stats","DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username","DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-functions"},"$$id-worker-mails":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name","_APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_SMTP_HOST=$$config__app_smtp_host","_APP_SMTP_PORT=$$config__app_smtp_port","_APP_SMTP_SECURE=$$config__app_smtp_secure","_APP_SMTP_USERNAME=$$config__app_smtp_username","_APP_SMTP_PASSWORD=$$secret__app_smtp_password","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-mails"},"$$id-worker-messaging":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_SMS_PROVIDER=$$config__app_sms_provider","_APP_SMS_FROM=$$config__app_sms_from","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-messaging"},"$$id-worker-webhooks":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-webhooks"}},"variables":[{"id":"$$config__app_influxdb_host","name":"_APP_INFLUXDB_HOST","label":"InfluxDB | _APP_INFLUXDB_HOST","defaultValue":"$$id-influxdb","description":""},{"id":"$$config__app_influxdb_port","name":"_APP_INFLUXDB_PORT","label":"InfluxDB | _APP_INFLUXDB_PORT","defaultValue":"8086","description":"InfluxDB server TCP port."},{"id":"$$config__app_env","name":"_APP_ENV","label":"General | _APP_ENV","defaultValue":"production","description":"Set your server running environment."},{"id":"$$config__app_worker_per_core","name":"_APP_WORKER_PER_CORE","label":"General | _APP_WORKER_PER_CORE","defaultValue":"6","description":"Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance."},{"id":"$$config__app_locale","name":"_APP_LOCALE","label":"General | _APP_LOCALE","defaultValue":"en","description":"Set your Appwrite's locale. By default, the locale is set to 'en'."},{"id":"$$config__app_console_whitelist_root","name":"_APP_CONSOLE_WHITELIST_ROOT","label":"General | _APP_CONSOLE_WHITELIST_ROOT","defaultValue":"enabled","description":"This option allows you to disable the creation of new users on the Appwrite console. When enabled only 1 user will be able to use the registration form. New users can be added by inviting them to your project. By default this option is enabled."},{"id":"$$config__app_console_whitelist_emails","name":"_APP_CONSOLE_WHITELIST_EMAILS","label":"General | _APP_CONSOLE_WHITELIST_EMAILS","defaultValue":"","description":"This option allows you to limit creation of new users on the Appwrite console. This option is very useful for small teams or sole developers. To enable it, pass a list of allowed email addresses separated by a comma."},{"id":"$$config__app_console_whitelist_ips","name":"_APP_CONSOLE_WHITELIST_IPS","label":"General | _APP_CONSOLE_WHITELIST_IPS","defaultValue":"","description":"This last option allows you to limit creation of users in Appwrite console for users sharing the same set of IP addresses. This option is very useful for team working with a VPN service or a company IP.\\n\\nTo enable/activate this option, pass a list of allowed IP addresses separated by a comma."},{"id":"$$config__app_system_email_name","name":"_APP_SYSTEM_EMAIL_NAME","label":"General | _APP_SYSTEM_EMAIL_NAME","defaultValue":"Appwrite","description":"This is the sender name value that will appear on email messages sent to developers from the Appwrite console. You can use url encoded strings for spaces and special chars."},{"id":"$$config__app_system_email_address","name":"_APP_SYSTEM_EMAIL_ADDRESS","label":"General | _APP_SYSTEM_EMAIL_ADDRESS","defaultValue":"team@appwrite.io","description":"This is the sender email address that will appear on email messages sent to developers from the Appwrite console. You should choose an email address that is allowed to be used from your SMTP server to avoid the server email ending in the users' SPAM folders."},{"id":"$$config__app_system_security_email_address","name":"_APP_SYSTEM_SECURITY_EMAIL_ADDRESS","label":"General | _APP_SYSTEM_SECURITY_EMAIL_ADDRESS","defaultValue":"certs@appwrite.io","description":"This is the email address used to issue SSL certificates for custom domains or the user agent in your webhooks payload."},{"id":"$$config__app_system_response_format","name":"_APP_SYSTEM_RESPONSE_FORMAT","label":"General | _APP_SYSTEM_RESPONSE_FORMAT","defaultValue":"","description":"Use this environment variable to set the default Appwrite HTTP response format to support an older version of Appwrite. This option is useful to overcome breaking changes between versions. You can also use the X-Appwrite-Response-Format HTTP request header to overwrite the response for a specific request. This variable accepts any valid Appwrite version. To use the current version format, leave the value of the variable empty."},{"id":"$$config__app_options_abuse","name":"_APP_OPTIONS_ABUSE","label":"General | _APP_OPTIONS_ABUSE","defaultValue":"enabled","description":"Allows you to disable abuse checks and API rate limiting. By default, set to 'enabled'. To cancel the abuse checking, set to 'disabled'. It is not recommended to disable this check-in a production environment."},{"id":"$$config__app_options_force_https","name":"_APP_OPTIONS_FORCE_HTTPS","label":"General | _APP_OPTIONS_FORCE_HTTPS","defaultValue":"disabled","description":"Allows you to force HTTPS connection to your API. This feature redirects any HTTP call to HTTPS and adds the 'Strict-Transport-Security' header to all HTTP responses."},{"id":"$$secret__app_openssl_key_v1","name":"_APP_OPENSSL_KEY_V1","label":"General | _APP_OPENSSL_KEY_V1","defaultValue":"$$generate_hex(256)","description":"This is your server private secret key that is used to encrypt all sensitive data on your server. Appwrite server encrypts all secret data on your server like webhooks, HTTP passwords, user sessions, and storage files. Keep it a secret and have a backup for it."},{"id":"$$config__app_domain","name":"_APP_DOMAIN","label":"General | _APP_DOMAIN","defaultValue":"$$generate_domain","description":"Your Appwrite domain address. When setting a public suffix domain, Appwrite will attempt to issue a valid SSL certificate automatically. When used with a dev domain, Appwrite will assign a self-signed SSL certificate. The default value is 'localhost'."},{"id":"$$config__app_domain_target","name":"_APP_DOMAIN_TARGET","label":"General | _APP_DOMAIN_TARGET","defaultValue":"$$generate_fqdn","description":"A DNS A record hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite '_APP_DOMAIN' variable. The default value is 'localhost'."},{"id":"$$config__app_redis_host","name":"_APP_REDIS_HOST","label":"Redis | _APP_REDIS_HOST","defaultValue":"$$id-redis","description":""},{"id":"$$config__app_redis_port","name":"_APP_REDIS_PORT","label":"Redis | _APP_REDIS_PORT","defaultValue":"6379","description":"Redis server TCP port."},{"id":"$$config__app_redis_user","name":"_APP_REDIS_USER","label":"Redis | _APP_REDIS_USER","defaultValue":"","description":"Redis server user. This is an optional variable. Default value is an empty string."},{"id":"$$secret__app_redis_pass","name":"_APP_REDIS_PASS","label":"Redis | _APP_REDIS_PASS","defaultValue":"","description":"Redis server password. This is an optional variable. Default value is an empty string."},{"id":"$$config__app_db_host","name":"_APP_DB_HOST","label":"MariaDB | _APP_DB_HOST","defaultValue":"$$id-mariadb","description":""},{"id":"$$config__app_db_port","name":"_APP_DB_PORT","label":"MariaDB | _APP_DB_PORT","defaultValue":"3306","description":"MariaDB server TCP port."},{"id":"$$config__app_db_schema","name":"_APP_DB_SCHEMA","label":"MariaDB | _APP_DB_SCHEMA","defaultValue":"appwrite","description":"MariaDB server database schema."},{"id":"$$config__app_db_user","name":"_APP_DB_USER","label":"MariaDB | _APP_DB_USER","defaultValue":"user","description":"MariaDB server user name."},{"id":"$$secret__app_db_root_pass","name":"MARIADB_ROOT_PASSWORD","label":"MariaDB | MARIADB_ROOT_PASSWORD","defaultValue":"$$generate_hex(16)","description":"MariaDB server root user password."},{"id":"$$secret__app_db_pass","name":"_APP_DB_PASS","label":"MariaDB | _APP_DB_PASS","defaultValue":"$$generate_hex(16)","description":"MariaDB server user password."},{"id":"$$config__app_smtp_host","name":"_APP_SMTP_HOST","label":"SMTP | _APP_SMTP_HOST","defaultValue":"","description":"SMTP server host name address. Use an empty string to disable all mail sending from the server. The default value for this variable is an empty string."},{"id":"$$config__app_smtp_port","name":"_APP_SMTP_PORT","label":"SMTP | _APP_SMTP_PORT","defaultValue":"","description":"SMTP server TCP port. Empty by default."},{"id":"$$config__app_smtp_secure","name":"_APP_SMTP_SECURE","label":"SMTP | _APP_SMTP_SECURE","defaultValue":"","description":"SMTP secure connection protocol. Empty by default, change to 'tls' if running on a secure connection."},{"id":"$$config__app_smtp_username","name":"_APP_SMTP_USERNAME","label":"SMTP | _APP_SMTP_USERNAME","defaultValue":"","description":"SMTP server user name. Empty by default."},{"id":"$$secret__app_smtp_password","name":"_APP_SMTP_PASSWORD","label":"SMTP | _APP_SMTP_PASSWORD","defaultValue":"","description":"SMTP server user password. Empty by default."},{"id":"$$config__app_usage_stats","name":"_APP_USAGE_STATS","label":"General | _APP_USAGE_STATS","defaultValue":"enabled","description":"This variable allows you to disable the collection and displaying of usage stats. This value is set to 'enabled' by default, to disable the usage stats set the value to 'disabled'. When disabled, it's recommended to turn off the Worker Usage, Influxdb and Telegraf containers for better resource usage."},{"id":"$$config__app_storage_limit","name":"_APP_STORAGE_LIMIT","label":"Storage | _APP_STORAGE_LIMIT","defaultValue":"30000000","description":"Maximum file size allowed for file upload. The default value is 30MB. You should pass your size limit value in bytes."},{"id":"$$config__app_storage_preview_limit","name":"_APP_STORAGE_PREVIEW_LIMIT","label":"Storage | _APP_STORAGE_PREVIEW_LIMIT","defaultValue":"20000000","description":"Maximum file size allowed for file image preview. The default value is 20MB. You should pass your size limit value in bytes."},{"id":"$$config__app_storage_antivirus_enabled","name":"_APP_STORAGE_ANTIVIRUS","label":"Storage | _APP_STORAGE_ANTIVIRUS","defaultValue":"disabled","description":"This variable allows you to disable the internal anti-virus scans. This value is set to 'disabled' by default, to enable the scans set the value to 'enabled'. Before enabling, you must add the ClamAV service and depend on it on main Appwrite service."},{"id":"$$config__app_storage_antivirus_host","name":"_APP_STORAGE_ANTIVIRUS_HOST","label":"Storage | _APP_STORAGE_ANTIVIRUS_HOST","defaultValue":"clamav","description":"ClamAV server host name address."},{"id":"$$config__app_storage_antivirus_port","name":"_APP_STORAGE_ANTIVIRUS_PORT","label":"Storage | _APP_STORAGE_ANTIVIRUS_PORT","defaultValue":"3310","description":"ClamAV server TCP port."},{"id":"$$config__app_storage_device","name":"_APP_STORAGE_DEVICE","label":"Storage | _APP_STORAGE_DEVICE","defaultValue":"Local","description":"Select default storage device. The default value is 'Local'. List of supported adapters are 'Local', 'S3', 'DOSpaces', 'Backblaze', 'Linode' and 'Wasabi'."},{"id":"$$secret__app_storage_s3_access_key","name":"_APP_STORAGE_S3_ACCESS_KEY","label":"Storage | _APP_STORAGE_S3_ACCESS_KEY","defaultValue":"","description":"AWS S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your AWS console."},{"id":"$$secret__app_storage_s3_secret","name":"_APP_STORAGE_S3_SECRET","label":"Storage | _APP_STORAGE_S3_SECRET","defaultValue":"","description":"AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console."},{"id":"$$config__app_storage_s3_region","name":"_APP_STORAGE_S3_REGION","label":"Storage | _APP_STORAGE_S3_REGION","defaultValue":"us-east-1","description":"AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console."},{"id":"$$config__app_storage_s3_bucket","name":"_APP_STORAGE_S3_BUCKET","label":"Storage | _APP_STORAGE_S3_BUCKET","defaultValue":"","description":"AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console."},{"id":"$$secret__app_storage_do_spaces_access_key","name":"_APP_STORAGE_DO_SPACES_ACCESS_KEY","label":"Storage | _APP_STORAGE_DO_SPACES_ACCESS_KEY","defaultValue":"","description":"DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console."},{"id":"$$secret__app_storage_do_spaces_secret","name":"_APP_STORAGE_DO_SPACES_SECRET","label":"Storage | _APP_STORAGE_DO_SPACES_SECRET","defaultValue":"","description":"DigitalOcean spaces secret key. Required when the storage adapter is set to DOSpaces. You can get your secret key from your DigitalOcean console."},{"id":"$$config__app_storage_do_spaces_region","name":"_APP_STORAGE_DO_SPACES_REGION","label":"Storage | _APP_STORAGE_DO_SPACES_REGION","defaultValue":"us-east-1","description":"DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console."},{"id":"$$config__app_storage_do_spaces_bucket","name":"_APP_STORAGE_DO_SPACES_BUCKET","label":"Storage | _APP_STORAGE_DO_SPACES_BUCKET","defaultValue":"","description":"DigitalOcean spaces bucket. Required when storage adapter is set to DOSpaces. You can create spaces in your DigitalOcean console."},{"id":"$$secret__app_storage_backblaze_access_key","name":"_APP_STORAGE_BACKBLAZE_ACCESS_KEY","label":"Storage | _APP_STORAGE_BACKBLAZE_ACCESS_KEY","defaultValue":"","description":"Backblaze access key. Required when the storage adapter is set to Backblaze. Your Backblaze keyID will be your access key. You can get your keyID from your Backblaze console."},{"id":"$$secret__app_storage_backblaze_secret","name":"_APP_STORAGE_BACKBLAZE_SECRET","label":"Storage | _APP_STORAGE_BACKBLAZE_SECRET","defaultValue":"","description":"Backblaze secret key. Required when the storage adapter is set to Backblaze. Your Backblaze applicationKey will be your secret key. You can get your applicationKey from your Backblaze console."},{"id":"$$config__app_storage_backblaze_region","name":"_APP_STORAGE_BACKBLAZE_REGION","label":"Storage | _APP_STORAGE_BACKBLAZE_REGION","defaultValue":"us-west-004","description":"Backblaze region. Required when storage adapter is set to Backblaze. You can find your region info from your Backblaze console."},{"id":"$$config__app_storage_backblaze_bucket","name":"_APP_STORAGE_BACKBLAZE_BUCKET","label":"Storage | _APP_STORAGE_BACKBLAZE_BUCKET","defaultValue":"","description":"Backblaze bucket. Required when storage adapter is set to Backblaze. You can create your bucket from your Backblaze console."},{"id":"$$secret__app_storage_linode_access_key","name":"_APP_STORAGE_LINODE_ACCESS_KEY","label":"Storage | _APP_STORAGE_LINODE_ACCESS_KEY","defaultValue":"","description":"Linode object storage access key. Required when the storage adapter is set to Linode. You can get your access key from your Linode console."},{"id":"$$secret__app_storage_linode_secret","name":"_APP_STORAGE_LINODE_SECRET","label":"Storage | _APP_STORAGE_LINODE_SECRET","defaultValue":"","description":"Linode object storage secret key. Required when the storage adapter is set to Linode. You can get your secret key from your Linode console."},{"id":"$$config__app_storage_linode_region","name":"_APP_STORAGE_LINODE_REGION","label":"Storage | _APP_STORAGE_LINODE_REGION","defaultValue":"eu-central-1","description":"Linode object storage region. Required when storage adapter is set to Linode. You can find your region info from your Linode console."},{"id":"$$config__app_storage_linode_bucket","name":"_APP_STORAGE_LINODE_BUCKET","label":"Storage | _APP_STORAGE_LINODE_BUCKET","defaultValue":"","description":"Linode object storage bucket. Required when storage adapter is set to Linode. You can create buckets in your Linode console."},{"id":"$$secret__app_storage_wasabi_access_key","name":"_APP_STORAGE_WASABI_ACCESS_KEY","label":"Storage | _APP_STORAGE_WASABI_ACCESS_KEY","defaultValue":"","description":"Wasabi access key. Required when the storage adapter is set to Wasabi. You can get your access key from your Wasabi console."},{"id":"$$secret__app_storage_wasabi_secret","name":"_APP_STORAGE_WASABI_SECRET","label":"Storage | _APP_STORAGE_WASABI_SECRET","defaultValue":"","description":"Wasabi secret key. Required when the storage adapter is set to Wasabi. You can get your secret key from your Wasabi console."},{"id":"$$config__app_storage_wasabi_region","name":"_APP_STORAGE_WASABI_REGION","label":"Storage | _APP_STORAGE_WASABI_REGION","defaultValue":"eu-central-1","description":"Wasabi region. Required when storage adapter is set to Wasabi. You can find your region info from your Wasabi console."},{"id":"$$config__app_storage_wasabi_bucket","name":"_APP_STORAGE_WASABI_BUCKET","label":"Storage | _APP_STORAGE_WASABI_BUCKET","defaultValue":"","description":"Wasabi bucket. Required when storage adapter is set to Wasabi. You can create buckets in your Wasabi console."},{"id":"$$config__app_functions_size_limit","name":"_APP_FUNCTIONS_SIZE_LIMIT","label":"Functions | _APP_FUNCTIONS_SIZE_LIMIT","defaultValue":"30000000","description":"The maximum size deployment in bytes. The default value is 30MB."},{"id":"$$config__app_functions_timeout","name":"_APP_FUNCTIONS_TIMEOUT","label":"Functions | _APP_FUNCTIONS_TIMEOUT","defaultValue":"900","description":"The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds."},{"id":"$$config__app_functions_build_timeout","name":"_APP_FUNCTIONS_BUILD_TIMEOUT","label":"Functions | _APP_FUNCTIONS_BUILD_TIMEOUT","defaultValue":"900","description":"The maximum number of seconds allowed as a timeout value when building a new function. The default value is 900 seconds."},{"id":"$$config__app_functions_containers","name":"_APP_FUNCTIONS_CONTAINERS","label":"Functions | _APP_FUNCTIONS_CONTAINERS","defaultValue":"10","description":"The maximum number of containers Appwrite is allowed to keep alive in the background for function environments. Running containers allow faster execution time as there is no need to recreate each container every time a function gets executed. The default value is 10."},{"id":"$$config__app_functions_cpus","name":"_APP_FUNCTIONS_CPUS","label":"Functions | _APP_FUNCTIONS_CPUS","defaultValue":"","description":"The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it's empty, CPU limit will be disabled."},{"id":"$$config__app_functions_memory_allocated","name":"_APP_FUNCTIONS_MEMORY","label":"Functions | _APP_FUNCTIONS_MEMORY","defaultValue":"","description":"The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it's empty, memory limit will be disabled."},{"id":"$$config__app_functions_memory_swap","name":"_APP_FUNCTIONS_MEMORY_SWAP","label":"Functions | _APP_FUNCTIONS_MEMORY_SWAP","defaultValue":"","description":"The maximum amount of swap memory a single cloud function is allowed to use in megabytes. The default value is empty. When it's empty, swap memory limit will be disabled."},{"id":"$$config__app_functions_runtimes","name":"_APP_FUNCTIONS_RUNTIMES","label":"Functions | _APP_FUNCTIONS_RUNTIMES","defaultValue":"node-18.0","description":"This option allows you to limit the available environments for cloud functions. This option is very useful for low-cost servers to safe disk space.\nTo enable/activate this option, pass a list of allowed environments separated by a comma.\nCurrently, supported environments are: node-14.5, node-16.0, node-18.0, php-8.0, php-8.1, ruby-3.0, ruby-3.1, python-3.8, python-3.9, python-3.10, deno-1.21, deno-1.24, dart-2.15, dart-2.16, dart-2.17, dotnet-3.1, dotnet-6.0, java-8.0, java-11.0, java-17.0, java-18.0, swift-5.5, kotlin-1.6, cpp-17.0"},{"id":"$$secret__app_executor_secret","name":"_APP_EXECUTOR_SECRET","label":"Functions | _APP_EXECUTOR_SECRET","defaultValue":"$$generate_hex(16)","description":"The secret key used by Appwrite to communicate with the function executor."},{"id":"$$config__app_executor_host","name":"_APP_EXECUTOR_HOST","label":"","defaultValue":"http://$$id-executor/v1","description":""},{"id":"$$config__app_logging_provider","name":"_APP_LOGGING_PROVIDER","label":"General | _APP_LOGGING_PROVIDER","defaultValue":"","description":"This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, to enable the logger set the value to one of 'sentry', 'raygun', 'appsignal', 'logowl'"},{"id":"$$config__app_logging_config","name":"_APP_LOGGING_CONFIG","label":"General | _APP_LOGGING_CONFIG","defaultValue":"","description":"This variable configures authentication to 3rd party error logging providers. If using Sentry, this should be 'SENTRY_API_KEY;SENTRY_APP_ID'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket."},{"id":"$$config__app_statsd_host","name":"_APP_STATSD_HOST","label":"","defaultValue":"$$id-telegraf","description":""},{"id":"$$config__app_statsd_port","name":"_APP_STATSD_PORT","label":"StatsD | _APP_STATSD_PORT","defaultValue":"8125","description":"StatsD server TCP port."},{"id":"$$config__app_maintenance_interval","name":"_APP_MAINTENANCE_INTERVAL","label":"Functions | _APP_MAINTENANCE_INTERVAL","defaultValue":"86400","description":"Interval value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 86400 seconds (1 day)."},{"id":"$$config__app_maintenance_retention_execution","name":"_APP_MAINTENANCE_RETENTION_EXECUTION","label":"Functions | _APP_MAINTENANCE_RETENTION_EXECUTION","defaultValue":"1209600","description":"The maximum duration (in seconds) upto which to retain execution logs. The default value is 1209600 seconds (14 days)."},{"id":"$$config__app_maintenance_retention_cache","name":"_APP_MAINTENANCE_RETENTION_CACHE","label":"Functions | _APP_MAINTENANCE_RETENTION_CACHE","defaultValue":"2592000","description":"The maximum duration (in seconds) upto which to retain cached files. The default value is 2592000 seconds (30 days)."},{"id":"$$config__app_maintenance_retention_abuse","name":"_APP_MAINTENANCE_RETENTION_ABUSE","label":"Functions | _APP_MAINTENANCE_RETENTION_ABUSE","defaultValue":"86400","description":"The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day)."},{"id":"$$config__app_maintenance_retention_audit","name":"_APP_MAINTENANCE_RETENTION_AUDIT","label":"Functions | _APP_MAINTENANCE_RETENTION_AUDIT","defaultValue":"1209600","description":"The maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days)."},{"id":"$$config__app_sms_provider","name":"_APP_SMS_PROVIDER","label":"Phone | _APP_SMS_PROVIDER","defaultValue":"","description":"Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'. Available providers are twilio, text-magic, telesign, msg91, and vonage."},{"id":"$$config__app_sms_from","name":"_APP_SMS_FROM","label":"Phone | _APP_SMS_FROM","defaultValue":"","description":"Phone number used for sending out messages. Must start with a leading '+' and maximum of 15 digits without spaces (+123456789)."},{"id":"$$config__app_functions_inactive_threshold","name":"_APP_FUNCTIONS_INACTIVE_THRESHOLD","label":"Functions | _APP_FUNCTIONS_INACTIVE_THRESHOLD","defaultValue":"60","description":"The minimum time a function can be inactive before it's container is shutdown and put to sleep. The default value is 60 seconds"},{"id":"$$config_open_runtimes_network","name":"OPEN_RUNTIMES_NETWORK","label":"","defaultValue":"$$generate_network","description":""},{"id":"$$config_dockerhub_pull_username","name":"DOCKERHUB_PULL_USERNAME","label":"Functions | DOCKERHUB_PULL_USERNAME","defaultValue":"","description":"The username for hub.docker.com. This variable is used to pull images from hub.docker.com."},{"id":"$$secret_dockerhub_pull_password","name":"DOCKERHUB_PULL_PASSWORD","label":"Functions | DOCKERHUB_PULL_PASSWORD","defaultValue":"","description":"The password for hub.docker.com. This variable is used to pull images from hub.docker.com."},{"id":"$$config__app_usage_timeseries_interval","name":"_APP_USAGE_TIMESERIES_INTERVAL","label":"General | _APP_USAGE_TIMESERIES_INTERVAL","defaultValue":"30","description":"Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats and syncing it to mariadb from InfluxDB. The default value is 30 seconds."},{"id":"$$config__app_usage_database_interval","name":"_APP_USAGE_DATABASE_INTERVAL","label":"General | _APP_USAGE_DATABASE_INTERVAL","defaultValue":"900","description":"Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats from data in Appwrite Database. The default value is 15 minutes."}]},{"templateVersion":"1.0.0","defaultVersion":"latest","documentation":"https://docs.weblate.org/en/latest/admin/install/docker.html","description":"A copylefted libre software web-based continuous localization system.","type":"weblate","name":"Weblate","labels":["translate","localization"],"services":{"$$id":{"name":"Weblate","depends_on":["$$id-postgresql","$$id-redis"],"image":"weblate/weblate:$$core_version","volumes":["$$id-data:/app/data"],"environment":["WEBLATE_SITE_DOMAIN=$$config_weblate_site_domain","WEBLATE_ADMIN_PASSWORD=$$secret_weblate_admin_password","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_USER=$$config_postgres_user","POSTGRES_DATABASE=$$config_postgres_db","POSTGRES_HOST=$$id-postgresql","POSTGRES_PORT=5432","REDIS_HOST=$$id-redis"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]},"$$id-redis":{"name":"Redis","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-redis-data:/data"],"environment":[],"ports":[]}},"variables":[{"id":"$$config_weblate_site_domain","name":"WEBLATE_SITE_DOMAIN","label":"Weblate Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$secret_weblate_admin_password","name":"WEBLATE_ADMIN_PASSWORD","label":"Weblate Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"weblate","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"2022.12.12-966e9c3c","documentation":"https://docs.searxng.org/","type":"searxng","name":"SearXNG","description":"Free internet metasearch engine which aggregates results from more than 70 search services.","services":{"$$id":{"name":"SearXNG","depends_on":["$$id-redis"],"image":"searxng/searxng:$$core_version","volumes":["$$id-searxng:/etc/searxng"],"environment":["SEARXNG_BASE_URL=$$config_searxng_base_url"],"ports":["8080"],"cap_drop":["ALL"],"cap_add":["CHOWN","SETGID","SETUID","DAC_OVERRIDE"],"files":[{"location":"/etc/searxng/settings.yml","content":"\n # see https://docs.searxng.org/admin/engines/settings.html#use-default-settings\n use_default_settings: true\n server:\n secret_key: $$secret_secret_key\n limiter: true\n image_proxy: true\n ui:\n static_use_hash: true\n redis:\n url: redis://:$$secret_redis_password@$$id-redis:6379/0"}]},"$$id-redis":{"name":"Redis","command":"redis-server --requirepass $$secret_redis_password --save \"\" --appendonly \"no\"","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-redis-data:/data"],"environment":["REDIS_PASSWORD=$$secret_redis_password"],"ports":[],"cap_drop":["ALL"],"cap_add":["SETGID","SETUID","DAC_OVERRIDE"]}},"variables":[{"id":"$$config_searxng_base_url","name":"SEARXNG_BASE_URL","label":"SearXNG Base URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_secret_key","name":"SECRET_KEY","label":"Secret Key","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$secret_redis_password","name":"REDIS_PASSWORD","label":"Redis Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v3.0.0","documentation":"https://glitchtip.com/documentation","type":"glitchtip","name":"GlitchTip","description":"Simple, open source error tracking.","labels":["sentry","bugsnag"],"services":{"$$id":{"name":"GlitchTip","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","volumes":[],"environment":["PORT=$$config_port","GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url","EMAIL_HOST=$$config_email_host","EMAIL_PORT=$$config_email_port","EMAIL_HOST_USER=$$config_email_host_user","EMAIL_HOST_PASSWORD=$$secret_email_host_password","EMAIL_USE_TLS=$$config_email_use_tls","EMAIL_USE_SSL=$$config_email_use_ssl","EMAIL_BACKEND=$$config_email_backend","MAILGUN_API_KEY=$$secret_mailgun_api_key","SENDGRID_API_KEY=$$secret_sendgrid_api_key","ENABLE_OPEN_USER_REGISTRATION=$$config_enable_open_user_registration","DJANGO_SUPERUSER_EMAIL=$$config_django_superuser_email","DJANGO_SUPERUSER_PASSWORD=$$secret_django_superuser_password","DJANGO_SUPERUSER_USERNAME=$$config_django_superuser_username","CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency"],"ports":["8000"]},"$$id-worker":{"name":"Celery Worker","command":"./bin/run-celery-with-beat.sh","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","environment":["GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url","CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency"],"ports":[]},"$$id-migrate":{"exclude":true,"name":"Migrate","command":"./manage.py migrate","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","environment":["GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url"],"ports":[]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]},"$$id-redis":{"name":"Redis","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-postgresql-redis-data:/data"],"environment":[],"ports":[]}},"variables":[{"id":"$$config_django_superuser_username","name":"DJANGO_SUPERUSER_USERNAME","label":"Django Superuser Username","defaultValue":"$$generate_username","description":""},{"id":"$$secret_django_superuser_password","name":"DJANGO_SUPERUSER_PASSWORD","label":"Django Superuser Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_port","name":"PORT","label":"GlitchTip Port","defaultValue":"8000","description":""},{"id":"$$config_celery_worker_concurrency","main":"$$id-worker","name":"CELERY_WORKER_CONCURRENCY","label":"Celery Worker Concurrency","defaultValue":"2","description":""},{"id":"$$config_glitchtip_domain","name":"GLITCHTIP_DOMAIN","label":"GlitchTip Domain","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_email_url","name":"EMAIL_URL","label":"SMTP Email URL","defaultValue":"smtp://$$config_email_host_user:$$secret_email_host_password@$$config_email_host:$$config_email_port","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_redis_url","name":"REDIS_URL","label":"Redis URL","defaultValue":"redis://$$id-redis:6379/0","description":""},{"id":"$$config_default_from_email","name":"DEFAULT_FROM_EMAIL","label":"Default Email Address","defaultValue":"noreply@example.com","description":""},{"id":"$$config_email_host","name":"EMAIL_HOST","label":"Email SMTP Host","defaultValue":"","description":""},{"id":"$$config_email_port","name":"EMAIL_PORT","label":"Email SMTP Port","defaultValue":"25","description":""},{"id":"$$config_email_host_user","name":"EMAIL_HOST_USER","label":"Email SMTP User","defaultValue":"","description":""},{"id":"$$secret_email_host_password","name":"EMAIL_HOST_PASSWORD","label":"Email SMTP Password","defaultValue":"","description":""},{"id":"$$config_email_use_tls","name":"EMAIL_USE_TLS","label":"Email Use TLS","defaultValue":"false","description":""},{"id":"$$config_email_use_ssl","name":"EMAIL_USE_SSL","label":"Email Use SSL","defaultValue":"false","description":""},{"id":"$$secret_email_smtp_password","name":"EMAIL_SMTP_PASSWORD","label":"SMTP Password","defaultValue":"","description":""},{"id":"$$config_email_backend","name":"EMAIL_BACKEND","label":"Email Backend","defaultValue":"","description":""},{"id":"$$secret_mailgun_api_key","name":"MAILGUN_API_KEY","label":"Mailgun API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$secret_sendgrid_api_key","name":"SENDGRID_API_KEY","label":"Sendgrid API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_enable_open_user_registration","name":"ENABLE_OPEN_USER_REGISTRATION","label":"Enable Open User Registration","defaultValue":"true","description":""},{"id":"$$config_django_superuser_email","name":"DJANGO_SUPERUSER_EMAIL","label":"Django Superuser Email","defaultValue":"noreply@example.com","description":""},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"glitchtip","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v2.16.0","documentation":"https://hasura.io/docs/latest/index/","type":"hasura","name":"Hasura","description":"Instant realtime GraphQL APIs on any Postgres application, existing or new.","labels":["graphql","database"],"services":{"$$id":{"name":"Hasura","depends_on":["$$id-postgresql"],"image":"hasura/graphql-engine:$$core_version","volumes":[],"environment":["HASURA_GRAPHQL_ENABLE_CONSOLE=$$config_hasura_graphql_enable_console","HASURA_GRAPHQL_METADATA_DATABASE_URL=$$secret_hasura_graphql_metadata_database_url","HASURA_GRAPHQL_ADMIN_SECRET=$$secret_hasura_graphql_admin_secret"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]}},"variables":[{"id":"$$config_hasura_graphql_enable_console","name":"HASURA_GRAPHQL_ENABLE_CONSOLE","label":"Enable Hasura Console","defaultValue":"true","description":""},{"id":"$$secret_hasura_graphql_metadata_database_url","name":"HASURA_GRAPHQL_METADATA_DATABASE_URL","label":"Hasura Metadata Database URL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hasura_graphql_admin_secret","name":"HASURA_GRAPHQL_ADMIN_SECRET","label":"Hasura Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"hasura","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"postgresql-v1.39.5","documentation":"https://umami.is/docs/getting-started","type":"umami-postgresql","name":"Umami","subname":"(PostgreSQL)","description":"A simple, easy to use, self-hosted web analytics solution.","services":{"$$id":{"name":"Umami","depends_on":["$$id-postgresql"],"image":"ghcr.io/umami-software/umami:$$core_version","volumes":[],"environment":["ADMIN_PASSWORD=$$secret_admin_password","DATABASE_URL=$$secret_database_url","DATABASE_TYPE=$$config_database_type","HASH_SALT=$$secret_hash_salt"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[],"files":[{"location":"/docker-entrypoint-initdb.d/schema.postgresql.sql","content":"\n -- CreateTable\n CREATE TABLE \"account\" (\n \"user_id\" SERIAL NOT NULL,\n \"username\" VARCHAR(255) NOT NULL,\n \"password\" VARCHAR(60) NOT NULL,\n \"is_admin\" BOOLEAN NOT NULL DEFAULT false,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"updated_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n\n PRIMARY KEY (\"user_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"event\" (\n \"event_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"event_type\" VARCHAR(50) NOT NULL,\n \"event_value\" VARCHAR(50) NOT NULL,\n\n PRIMARY KEY (\"event_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"pageview\" (\n \"view_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"referrer\" VARCHAR(500),\n\n PRIMARY KEY (\"view_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"session\" (\n \"session_id\" SERIAL NOT NULL,\n \"session_uuid\" UUID NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"hostname\" VARCHAR(100),\n \"browser\" VARCHAR(20),\n \"os\" VARCHAR(20),\n \"device\" VARCHAR(20),\n \"screen\" VARCHAR(11),\n \"language\" VARCHAR(35),\n \"country\" CHAR(2),\n\n PRIMARY KEY (\"session_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"website\" (\n \"website_id\" SERIAL NOT NULL,\n \"website_uuid\" UUID NOT NULL,\n \"user_id\" INTEGER NOT NULL,\n \"name\" VARCHAR(100) NOT NULL,\n \"domain\" VARCHAR(500),\n \"share_id\" VARCHAR(64),\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n\n PRIMARY KEY (\"website_id\")\n );\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"account.username_unique\" ON \"account\"(\"username\");\n\n -- CreateIndex\n CREATE INDEX \"event_created_at_idx\" ON \"event\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"event_session_id_idx\" ON \"event\"(\"session_id\");\n\n -- CreateIndex\n CREATE INDEX \"event_website_id_idx\" ON \"event\"(\"website_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_created_at_idx\" ON \"pageview\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_session_id_idx\" ON \"pageview\"(\"session_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_idx\" ON \"pageview\"(\"website_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_session_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"session_id\", \"created_at\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"session.session_uuid_unique\" ON \"session\"(\"session_uuid\");\n\n -- CreateIndex\n CREATE INDEX \"session_created_at_idx\" ON \"session\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"session_website_id_idx\" ON \"session\"(\"website_id\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"website.website_uuid_unique\" ON \"website\"(\"website_uuid\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"website.share_id_unique\" ON \"website\"(\"share_id\");\n\n -- CreateIndex\n CREATE INDEX \"website_user_id_idx\" ON \"website\"(\"user_id\");\n\n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"session\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"website\" ADD FOREIGN KEY (\"user_id\") REFERENCES \"account\"(\"user_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);"}]}},"variables":[{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hash_salt","name":"HASH_SALT","label":"Hash Salt","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_database_type","name":"DATABASE_TYPE","label":"Database Type","defaultValue":"postgresql","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"umami","description":""},{"id":"$$secret_admin_password","name":"ADMIN_PASSWORD","label":"Initial Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","ignore":true,"defaultVersion":"postgresql-v1.39.5","documentation":"https://umami.is/docs/getting-started","type":"umami","name":"Umami","subname":"(PostgreSQL)","description":"A simple, easy to use, self-hosted web analytics solution.","services":{"$$id":{"name":"Umami","depends_on":["$$id-postgresql"],"image":"ghcr.io/umami-software/umami:$$core_version","volumes":[],"environment":["ADMIN_PASSWORD=$$secret_admin_password","DATABASE_URL=$$secret_database_url","DATABASE_TYPE=$$config_database_type","HASH_SALT=$$secret_hash_salt"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[],"files":[{"location":"/docker-entrypoint-initdb.d/schema.postgresql.sql","content":"\n -- CreateTable\n CREATE TABLE \"account\" (\n \"user_id\" SERIAL NOT NULL,\n \"username\" VARCHAR(255) NOT NULL,\n \"password\" VARCHAR(60) NOT NULL,\n \"is_admin\" BOOLEAN NOT NULL DEFAULT false,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"updated_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n\n PRIMARY KEY (\"user_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"event\" (\n \"event_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"event_type\" VARCHAR(50) NOT NULL,\n \"event_value\" VARCHAR(50) NOT NULL,\n\n PRIMARY KEY (\"event_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"pageview\" (\n \"view_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"referrer\" VARCHAR(500),\n\n PRIMARY KEY (\"view_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"session\" (\n \"session_id\" SERIAL NOT NULL,\n \"session_uuid\" UUID NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"hostname\" VARCHAR(100),\n \"browser\" VARCHAR(20),\n \"os\" VARCHAR(20),\n \"device\" VARCHAR(20),\n \"screen\" VARCHAR(11),\n \"language\" VARCHAR(35),\n \"country\" CHAR(2),\n\n PRIMARY KEY (\"session_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"website\" (\n \"website_id\" SERIAL NOT NULL,\n \"website_uuid\" UUID NOT NULL,\n \"user_id\" INTEGER NOT NULL,\n \"name\" VARCHAR(100) NOT NULL,\n \"domain\" VARCHAR(500),\n \"share_id\" VARCHAR(64),\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n\n PRIMARY KEY (\"website_id\")\n );\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"account.username_unique\" ON \"account\"(\"username\");\n\n -- CreateIndex\n CREATE INDEX \"event_created_at_idx\" ON \"event\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"event_session_id_idx\" ON \"event\"(\"session_id\");\n\n -- CreateIndex\n CREATE INDEX \"event_website_id_idx\" ON \"event\"(\"website_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_created_at_idx\" ON \"pageview\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_session_id_idx\" ON \"pageview\"(\"session_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_idx\" ON \"pageview\"(\"website_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_session_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"session_id\", \"created_at\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"session.session_uuid_unique\" ON \"session\"(\"session_uuid\");\n\n -- CreateIndex\n CREATE INDEX \"session_created_at_idx\" ON \"session\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"session_website_id_idx\" ON \"session\"(\"website_id\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"website.website_uuid_unique\" ON \"website\"(\"website_uuid\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"website.share_id_unique\" ON \"website\"(\"share_id\");\n\n -- CreateIndex\n CREATE INDEX \"website_user_id_idx\" ON \"website\"(\"user_id\");\n\n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"session\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"website\" ADD FOREIGN KEY (\"user_id\") REFERENCES \"account\"(\"user_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);"}]}},"variables":[{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hash_salt","name":"HASH_SALT","label":"Hash Salt","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_database_type","name":"DATABASE_TYPE","label":"Database Type","defaultValue":"postgresql","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"umami","description":""},{"id":"$$secret_admin_password","name":"ADMIN_PASSWORD","label":"Initial Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"v0.30.1","documentation":"https://docs.meilisearch.com/learn/getting_started/quick_start.html","type":"meilisearch","name":"MeiliSearch","description":"A lightning Fast, Ultra Relevant, and Typo-Tolerant Search Engine.","services":{"$$id":{"name":"MeiliSearch","documentation":"https://docs.meilisearch.com/","depends_on":[],"image":"getmeili/meilisearch:$$core_version","volumes":["$$id-datams:/meili_data/data.ms","$$id-data:/meili_data","$$id-snapshot:/snapshot","$$id-dump:/dumps"],"environment":["MEILI_MASTER_KEY=$$secret_meili_master_key"],"ports":["7700"]}},"variables":[{"id":"$$secret_meili_master_key","name":"MEILI_MASTER_KEY","label":"Master Key","defaultValue":"$$generate_hex(64)","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","ignore":true,"defaultVersion":"latest","documentation":"https://docs.ghost.org","arch":"amd64","type":"ghost-mariadb","name":"Ghost","subname":"(MariaDB)","description":"Free and open source blogging platform.","labels":["cms","blog"],"services":{"$$id":{"name":"Ghost","depends_on":["$$id-mariadb"],"image":"bitnami/ghost:$$core_version","volumes":["$$id-ghost:/bitnami/ghost"],"environment":["url=$$config_url","GHOST_HOST=$$config_ghost_host","GHOST_ENABLE_HTTPS=$$config_ghost_enable_https","GHOST_EMAIL=$$config_ghost_email","GHOST_PASSWORD=$$secret_ghost_password","GHOST_DATABASE_HOST=$$config_ghost_database_host","GHOST_DATABASE_USER=$$config_mariadb_user","GHOST_DATABASE_PASSWORD=$$secret_ghost_database_password","GHOST_DATABASE_NAME=$$config_mariadb_database","GHOST_DATABASE_PORT_NUMBER=3306"],"ports":["2368"]},"$$id-mariadb":{"name":"MariaDB","depends_on":[],"image":"bitnami/mariadb:latest","volumes":["$$id-mariadb:/bitnami/mariadb"],"environment":["MARIADB_USER=$$config_mariadb_user","MARIADB_PASSWORD=$$secret_mariadb_password","MARIADB_DATABASE=$$config_mariadb_database","MARIADB_ROOT_USER=$$config_mariadb_root_user","MARIADB_ROOT_PASSWORD=$$secret_mariadb_root_password"],"ports":[]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_ghost_host","name":"GHOST_HOST","label":"Ghost Host","defaultValue":"$$generate_domain","description":""},{"id":"$$config_ghost_enable_https","name":"GHOST_ENABLE_HTTPS","label":"Ghost Enable HTTPS","defaultValue":"no","description":""},{"id":"$$config_ghost_email","name":"GHOST_EMAIL","label":"Ghost Default Email","defaultValue":"admin@example.com","description":""},{"id":"$$secret_ghost_password","name":"GHOST_PASSWORD","label":"Ghost Default Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_ghost_database_host","name":"GHOST_DATABASE_HOST","label":"Ghost Database Host","defaultValue":"$$id-mariadb","description":""},{"id":"$$config_ghost_database_user","name":"GHOST_DATABASE_USER","label":"MariaDB User","defaultValue":"$$config_mariadb_user","description":""},{"id":"$$secret_ghost_database_password","name":"GHOST_DATABASE_PASSWORD","label":"MariaDB Password","defaultValue":"$$secret_mariadb_password","description":""},{"id":"$$config_ghost_database_name","name":"GHOST_DATABASE_NAME","label":"MariaDB Database","defaultValue":"$$config_mariadb_database","description":""},{"id":"$$config_mariadb_user","name":"MARIADB_USER","label":"MariaDB User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mariadb_password","name":"MARIADB_PASSWORD","label":"MariaDB Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_mariadb_database","name":"MARIADB_DATABASE","label":"MariaDB Database","defaultValue":"ghost","description":""},{"id":"$$config_mariadb_root_user","name":"MARIADB_ROOT_USER","label":"MariaDB Root User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mariadb_root_password","name":"MARIADB_ROOT_PASSWORD","label":"MariaDB Root Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"5.25.3","documentation":"https://docs.ghost.org","type":"ghost-only","name":"Ghost","subname":"(without Database)","description":"Free and open source blogging platform.","services":{"$$id":{"name":"Ghost","image":"ghost:$$core_version","volumes":["$$id-ghost:/var/lib/ghost/content"],"environment":["url=$$config_url","database__client=$$config_database__client","database__connection__host=$$config_database__connection__host","database__connection__user=$$config_database__connection__user","database__connection__password=$$secret_database__connection__password","database__connection__database=$$config_database__connection__database"],"ports":["2368"]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_database__client","name":"database__client","label":"Database Client","defaultValue":"mysql","description":"","required":true},{"id":"$$config_database__connection__host","name":"database__connection__host","label":"Database Host","defaultValue":"","description":"","required":true,"placeholder":"db.coolify.io"},{"id":"$$config_database__connection__user","name":"database__connection__user","label":"Database User","defaultValue":"","description":"","placeholder":"ghost","required":true},{"id":"$$secret_database__connection__password","name":"database__connection__password","label":"Database Password","defaultValue":"","description":"","placeholder":"superSecretP4ssword","showOnConfiguration":true,"required":true},{"id":"$$config_database__connection__database","name":"database__connection__database","label":"Database Name","defaultValue":"","description":"","placeholder":"ghost_db","required":true}]},{"templateVersion":"1.0.0","defaultVersion":"5.25.3","documentation":"https://docs.ghost.org","type":"ghost-mysql","name":"Ghost","subname":"(MySQL)","description":"Ghost is a free and open source blogging platform.","services":{"$$id":{"name":"Ghost","depends_on":["$$id-mysql"],"image":"ghost:$$core_version","volumes":["$$id-ghost:/var/lib/ghost/content"],"environment":["url=$$config_url","database__client=$$config_database__client","database__connection__host=$$config_database__connection__host","database__connection__user=$$config_mysql_user","database__connection__password=$$secret_mysql_password","database__connection__database=$$config_mysql_database"],"ports":["2368"]},"$$id-mysql":{"name":"MySQL","depends_on":[],"image":"mysql:8.0","volumes":["$$id-mysql:/var/lib/mysql"],"environment":["MYSQL_USER=$$config_mysql_user","MYSQL_PASSWORD=$$secret_mysql_password","MYSQL_DATABASE=$$config_mysql_database","MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password"],"ports":[]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_database__client","name":"database__client","label":"Database Client","defaultValue":"mysql","description":"","readOnly":true},{"id":"$$config_database__connection__host","name":"database__connection__host","label":"Database Host","defaultValue":"$$id-mysql","description":""},{"id":"$$config_mysql_user","main":"$$id-mysql","name":"MYSQL_USER","label":"MySQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mysql_password","main":"$$id-mysql","name":"MYSQL_PASSWORD","label":"MySQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_mysql_database","main":"$$id-mysql","name":"MYSQL_DATABASE","label":"MySQL Database","defaultValue":"ghost","description":""},{"id":"$$secret_mysql_root_password","name":"MYSQL_ROOT_PASSWORD","label":"MySQL Root Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"php8.1","documentation":"https://wordpress.org/","type":"wordpress","name":"WordPress","subname":"(MySQL)","description":"A content management system based on PHP.","labels":["wordpress","php","cms"],"services":{"$$id":{"name":"WordPress","depends_on":["$$id-mysql"],"image":"wordpress:$$core_version","volumes":["$$id-wordpress-data:/var/www/html"],"environment":["WORDPRESS_DB_HOST=$$config_wordpress_db_host","WORDPRESS_DB_USER=$$config_mysql_user","WORDPRESS_DB_PASSWORD=$$secret_mysql_password","WORDPRESS_DB_NAME=$$config_mysql_database","WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra"],"ports":["80"]},"$$id-mysql":{"name":"MySQL","depends_on":[],"image":"bitnami/mysql:5.7","imageArm":"mysql:8.0","volumes":["$$id-mysql-data:/bitnami/mysql/data"],"volumesArm":["$$id-mysql-data:/var/lib/mysql"],"environment":["MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password","MYSQL_ROOT_USER=$$config_mysql_root_user","MYSQL_DATABASE=$$config_mysql_database","MYSQL_USER=$$config_mysql_user","MYSQL_PASSWORD=$$secret_mysql_password"]}},"variables":[{"id":"$$config_wordpress_db_host","name":"WORDPRESS_DB_HOST","label":"Database Host","defaultValue":"$$id-mysql","description":"","readOnly":true},{"id":"$$config_wordpress_config_extra","name":"WORDPRESS_CONFIG_EXTRA","label":"WordPress Config Extra","defaultValue":"","description":"","type":"textarea","placeholder":"define('WP_DEBUG', true);\ndefine('WP_DEBUG_LOG', true);\ndefine('WP_DEBUG_DISPLAY', false);\n@ini_set('display_errors', 0);\n"},{"id":"$$secret_mysql_root_password","name":"MYSQL_ROOT_PASSWORD","label":"MySQL Root Password","defaultValue":"$$generate_password","description":"","readOnly":true},{"id":"$$config_mysql_root_user","name":"MYSQL_ROOT_USER","label":"MySQL Root User","defaultValue":"$$generate_username","description":"","readOnly":true},{"id":"$$config_mysql_database","name":"MYSQL_DATABASE","label":"MySQL Database","defaultValue":"wordpress","description":"","readOnly":true},{"id":"$$config_mysql_user","name":"MYSQL_USER","label":"MySQL User","defaultValue":"$$generate_username","description":"","readOnly":true},{"id":"$$secret_mysql_password","name":"MYSQL_PASSWORD","label":"MySQL Password","defaultValue":"$$generate_password","description":"","readOnly":true}]},{"templateVersion":"1.0.0","defaultVersion":"php8.1","documentation":"https://wordpress.org/","type":"wordpress-only","name":"WordPress","subname":"(without DB)","description":"A content management system based on PHP.","labels":["wordpress","php","cms"],"services":{"$$id":{"name":"WordPress","image":"wordpress:$$core_version","volumes":["$$id-wordpress-data:/var/www/html"],"environment":["WORDPRESS_DB_HOST=$$config_wordpress_db_host","WORDPRESS_DB_PORT=$$config_wordpress_db_port","WORDPRESS_DB_USER=$$config_wordpress_db_user","WORDPRESS_DB_PASSWORD=$$secret_wordpress_db_password","WORDPRESS_DB_NAME=$$config_wordpress_db_name","WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra"],"ports":["80"]}},"variables":[{"id":"$$config_wordpress_db_host","name":"WORDPRESS_DB_HOST","label":"Database Host","defaultValue":"","description":"","placeholder":"db.coollabs.io","required":true},{"id":"$$config_wordpress_db_port","name":"WORDPRESS_DB_PORT","label":"Database Port","defaultValue":"","description":"","placeholder":"3306","required":true},{"id":"$$config_wordpress_db_user","name":"WORDPRESS_DB_USER","label":"Database User","defaultValue":"","description":"","placeholder":"wordpress","required":true},{"id":"$$secret_wordpress_db_password","name":"WORDPRESS_DB_PASSWORD","label":"Database Password","defaultValue":"","description":"","placeholder":"supers3cr3tpassw0rd!","required":true,"showOnConfiguration":true},{"id":"$$config_wordpress_db_name","name":"WORDPRESS_DB_NAME","label":"Database Name","defaultValue":"","description":"","placeholder":"wordpress","required":true},{"id":"$$config_wordpress_config_extra","name":"WORDPRESS_CONFIG_EXTRA","label":"Extra Config","defaultValue":"","description":"","type":"textarea","placeholder":"define('WP_DEBUG', true);\ndefine('WP_DEBUG_LOG', true);\ndefine('WP_DEBUG_DISPLAY', false);\n@ini_set('display_errors', 0);\n"}]},{"templateVersion":"1.0.0","defaultVersion":"4.9.0","documentation":"https://coder.com/docs/coder-oss/latest","type":"vscodeserver","name":"VSCode Server","description":"Visual Studio Code on a remote server, accessible through the browser.","labels":["vscode","ide"],"services":{"$$id":{"name":"VSCode Server","depends_on":[],"image":"codercom/code-server:$$core_version","volumes":["$$id-config-data:/home/coder/.local/share/code-server","$$id-vscodeserver-data:/home/coder","$$id-keys-directory:/root/.ssh","$$id-theme-and-plugin-directory:/root/.local/share/code-server"],"environment":["PASSWORD=$$secret_password"],"ports":["8080"]}},"variables":[{"id":"$$secret_password","name":"PASSWORD","label":"Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"RELEASE.2022-12-12T19-27-27Z","documentation":"https://min.io/docs/minio","type":"minio","name":"MinIO","description":"A cloud storage server compatible with Amazon S3.","labels":["storage","s3"],"services":{"$$id":{"name":"MinIO","command":"server /data --console-address :9001","depends_on":[],"image":"minio/minio:$$core_version","volumes":["$$id-minio-data:/data","$$id-data-write:/files"],"environment":["MINIO_SERVER_URL=$$config_coolify_fqdn_minio_console","MINIO_BROWSER_REDIRECT_URL=$$config_minio_browser_redirect_url","MINIO_DOMAIN=$$config_minio_domain","MINIO_ROOT_USER=$$config_minio_root_user","MINIO_ROOT_PASSWORD=$$secret_minio_root_password"],"ports":["9000","9001"],"proxy":[{"port":"9000","domain":"$$config_coolify_fqdn_minio_console"},{"port":"9001"}]}},"variables":[{"id":"$$config_coolify_fqdn_minio_console","name":"MINIO_SERVER_URL","label":"MinIO Server URL","defaultValue":"","description":"Specify the URL hostname the MinIO Console should use for connecting to the MinIO Server.","required":true},{"id":"$$config_minio_browser_redirect_url","name":"MINIO_BROWSER_REDIRECT_URL","label":"Browser Redirect URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_minio_domain","name":"MINIO_DOMAIN","label":"Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_minio_root_user","name":"MINIO_ROOT_USER","label":"Root User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_minio_root_password","name":"MINIO_ROOT_PASSWORD","label":"Root User Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"0.21.1","documentation":"https://fider.io/docs","type":"fider","name":"Fider","description":"A platform to collect and organize customer feedback.","labels":["suggestion","feedback"],"services":{"$$id":{"name":"Fider","image":"getfider/fider:$$core_version","depends_on":["$$id-postgresql"],"environment":["BASE_URL=$$config_base_url","DATABASE_URL=$$secret_database_url","JWT_SECRET=$$secret_jwt_secret","EMAIL_NOREPLY=$$config_email_noreply","EMAIL_MAILGUN_API=$$secret_email_mailgun_api","EMAIL_MAILGUN_REGION=$$config_email_mailgun_region","EMAIL_MAILGUN_DOMAIN=$$config_email_mailgun_domain","EMAIL_SMTP_HOST=$$config_email_smtp_host","EMAIL_SMTP_PORT=$$config_email_smtp_port","EMAIL_SMTP_USER=$$config_email_smtp_user","EMAIL_SMTP_PASSWORD=$$secret_email_smtp_password","EMAIL_SMTP_ENABLE_STARTTLS=$$config_email_smtp_enable_starttls"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db?sslmode=disable","description":""},{"id":"$$secret_jwt_secret","name":"JWT_SECRET","label":"JWT Secret","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_email_noreply","name":"EMAIL_NOREPLY","label":"No Reply Email Address","defaultValue":"noreply@example.com","description":""},{"id":"$$secret_email_mailgun_api","name":"EMAIL_MAILGUN_API","label":"Mailgun API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_email_mailgun_region","name":"EMAIL_MAILGUN_REGION","label":"Mailgun Region","defaultValue":"EU","description":""},{"id":"$$config_email_mailgun_domain","name":"EMAIL_MAILGUN_DOMAIN","label":"Mailgun Domain","defaultValue":"","description":""},{"id":"$$config_email_smtp_host","name":"EMAIL_SMTP_HOST","label":"SMTP Host","defaultValue":"","description":""},{"id":"$$config_email_smtp_port","name":"EMAIL_SMTP_PORT","label":"SMTP Port","defaultValue":"587","description":""},{"id":"$$config_email_smtp_user","name":"EMAIL_SMTP_USER","label":"SMTP User","defaultValue":"","description":""},{"id":"$$secret_email_smtp_password","name":"EMAIL_SMTP_PASSWORD","label":"SMTP Password","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_email_smtp_enable_starttls","name":"EMAIL_SMTP_ENABLE_STARTTLS","label":"SMTP Enable StartTLS","defaultValue":"false","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"$$generate_username","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"0.207.0","documentation":"https://docs.n8n.io","type":"n8n","name":"n8n.io","description":"A free and open node based Workflow Automation Tool.","labels":["workflow","automation","ifttt","zapier","nodered"],"services":{"$$id":{"name":"N8n","depends_on":[],"image":"n8nio/n8n:$$core_version","volumes":["$$id-data:/root/.n8n","$$id-data-write:/files","/var/run/docker.sock:/var/run/docker.sock"],"environment":["WEBHOOK_URL=$$config_webhook_url"],"ports":["5678"]}},"variables":[{"id":"$$config_webhook_url","name":"WEBHOOK_URL","label":"Webhook URL","defaultValue":"$$generate_fqdn","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"stable","documentation":"https://plausible.io/doc/","arch":"amd64","type":"plausibleanalytics","name":"Plausible Analytics","description":"A lightweight and open-source website analytics tool.","labels":["analytics","statistics","plausible","gdpr","no-cookie","google analytics"],"services":{"$$id":{"name":"Plausible Analytics","command":"sh -c \"sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run\"","depends_on":["$$id-postgresql","$$id-clickhouse"],"image":"plausible/analytics:$$core_version","environment":["ADMIN_USER_EMAIL=$$config_admin_user_email","ADMIN_USER_NAME=$$config_admin_user_name","ADMIN_USER_PWD=$$secret_admin_user_pwd","BASE_URL=$$config_base_url","SECRET_KEY_BASE=$$secret_secret_key_base","DISABLE_AUTH=$$config_disable_auth","DISABLE_REGISTRATION=$$config_disable_registration","DATABASE_URL=$$secret_database_url","CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url"],"ports":["8000"]},"$$id-postgresql":{"name":"PostgreSQL","image":"bitnami/postgresql:13","volumes":["$$id-postgresql-data:/bitnami/postgresql"],"environment":["POSTGRESQL_PASSWORD=$$secret_postgresql_password","POSTGRESQL_USERNAME=$$config_postgresql_username","POSTGRESQL_DATABASE=$$config_postgresql_database"]},"$$id-clickhouse":{"name":"Clickhouse","volumes":["$$id-clickhouse-data:/var/lib/clickhouse"],"image":"clickhouse/clickhouse-server:22.6-alpine","ulimits":{"nofile":{"soft":262144,"hard":262144}},"files":[{"location":"/etc/clickhouse-server/users.d/logging.xml","content":"warningtrue"},{"location":"/etc/clickhouse-server/config.d/logging.xml","content":"00"},{"location":"/docker-entrypoint-initdb.d/init.query","content":"CREATE DATABASE IF NOT EXISTS plausible;"},{"location":"/docker-entrypoint-initdb.d/init-db.sh","content":"clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query"}]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":"You must set this to the FQDN of the Plausible Analytics instance. This is used to generate the links to the Plausible Analytics instance."},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgresql_username:$$secret_postgresql_password@$$id-postgresql:5432/$$config_postgresql_database","description":""},{"id":"$$secret_clickhouse_database_url","name":"CLICKHOUSE_DATABASE_URL","label":"Database URL for Clickhouse","defaultValue":"http://$$id-clickhouse:8123/plausible","description":""},{"id":"$$config_admin_user_email","name":"ADMIN_USER_EMAIL","label":"Admin Email Address","defaultValue":"admin@example.com","description":"This is the admin email. Please change it."},{"id":"$$config_admin_user_name","name":"ADMIN_USER_NAME","label":"Admin User Name","defaultValue":"$$generate_username","description":"This is the admin username. Please change it."},{"id":"$$secret_admin_user_pwd","name":"ADMIN_USER_PWD","label":"Admin User Password","defaultValue":"$$generate_password","description":"This is the admin password. Please change it.","showOnConfiguration":true},{"id":"$$secret_secret_key_base","name":"SECRET_KEY_BASE","label":"Secret Key Base","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_disable_auth","name":"DISABLE_AUTH","label":"Disable Authentication","defaultValue":"false","description":""},{"id":"$$config_disable_registration","name":"DISABLE_REGISTRATION","label":"Disable Registration","defaultValue":"true","description":""},{"id":"$$config_postgresql_username","main":"$$id-postgresql","name":"POSTGRESQL_USERNAME","label":"PostgreSQL Username","defaultValue":"postgresql","description":""},{"id":"$$secret_postgresql_password","main":"$$id-postgresql","name":"POSTGRESQL_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgresql_database","main":"$$id-postgresql","name":"POSTGRESQL_DATABASE","label":"PostgreSQL Database","defaultValue":"plausible","description":""},{"id":"$$config_scriptName","name":"SCRIPT_NAME","label":"Custom Script Name","defaultValue":"plausible.js","description":"This is the default script name."}]},{"templateVersion":"1.0.0","defaultVersion":"0.99.1","documentation":"https://docs.nocodb.com","type":"nocodb","name":"NocoDB","description":"Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadsheet.","labels":["database","airtable","spreadsheet"],"services":{"$$id":{"name":"NocoDB","image":"nocodb/nocodb:$$core_version","environment":["PORT=$$config_port","NC_DB=$$config_nc_db","DATABASE_URL=$$secret_database_url","NC_PUBLIC_URL=$$config_public_url","NC_AUTH_JWT_SECRET=$$secret_auth_jwt_secret","NC_SENTRY_DSN=$$secret_sentry_dsn","NC_CONNECT_TO_EXTERNAL_DB_DISABLED=$$config_connect_to_external_db_disabled","NC_DISABLE_TELE=$$config_disable_tele"],"volumes":["$$id-data:/usr/app/data"],"ports":["8080"]}},"variables":[{"id":"$$config_nc_db","name":"NC_DB","label":"Database","defaultValue":"","description":"MySQL, PostgreSQL and MSSQL connection urls supported. If absent: A local SQLite will be created in root folder."},{"id":"$$config_port","name":"PORT","label":"Port","defaultValue":"8080","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL","defaultValue":"","description":"JDBC URL Format. Can be used instead of NC_DB. Used in 1-Click Heroku deployment."},{"id":"$$config_public_url","name":"NC_PUBLIC_URL","label":"Public URL","defaultValue":"","description":"Used for sending Email invitations. If absent: Best guess from http request params."},{"id":"$$secret_auth_jwt_secret","name":"NC_AUTH_JWT_SECRET","label":"Auth JWT Secret","defaultValue":"$$generate_hex(64)","description":"JWT secret used for auth and storing other secrets. If absent: A Random secret will be generated."},{"id":"$$secret_sentry_dsn","name":"NC_SENTRY_DSN","label":"Sentry DSN","defaultValue":"","description":"For Sentry monitoring."},{"id":"$$config_connect_to_external_db_disabled","name":"NC_CONNECT_TO_EXTERNAL_DB_DISABLED","label":"Disable External Database","defaultValue":"0","description":"Disable Project creation with external database. (Enter \"1\" to disable)."},{"id":"$$config_disable_tele","name":"NC_DISABLE_TELE","label":"NocoDB Disable Telemetry","defaultValue":"1","description":"Disable telemetry (Enter \"1\" to disable)."}]}] \ No newline at end of file diff --git a/apps/client/.eslintignore b/apps/client/.eslintignore new file mode 100644 index 000000000..38972655f --- /dev/null +++ b/apps/client/.eslintignore @@ -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 diff --git a/apps/client/.eslintrc.cjs b/apps/client/.eslintrc.cjs new file mode 100644 index 000000000..3ccf435f0 --- /dev/null +++ b/apps/client/.eslintrc.cjs @@ -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 + } +}; diff --git a/apps/client/.gitignore b/apps/client/.gitignore new file mode 100644 index 000000000..6635cf554 --- /dev/null +++ b/apps/client/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/apps/client/.npmrc b/apps/client/.npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/apps/client/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/apps/client/.prettierignore b/apps/client/.prettierignore new file mode 100644 index 000000000..38972655f --- /dev/null +++ b/apps/client/.prettierignore @@ -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 diff --git a/apps/client/.prettierrc b/apps/client/.prettierrc new file mode 100644 index 000000000..a77fddea9 --- /dev/null +++ b/apps/client/.prettierrc @@ -0,0 +1,9 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte"], + "pluginSearchDirs": ["."], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] +} diff --git a/apps/client/README.md b/apps/client/README.md new file mode 100644 index 000000000..d7fe004d9 --- /dev/null +++ b/apps/client/README.md @@ -0,0 +1 @@ +# SvelteKit Static site diff --git a/apps/client/package.json b/apps/client/package.json new file mode 100644 index 000000000..b9fda399a --- /dev/null +++ b/apps/client/package.json @@ -0,0 +1,50 @@ +{ + "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", + "flowbite-svelte": "0.28.0", + "js-cookie": "3.0.1", + "server": "workspace:*", + "superjson": "1.11.0" + } +} diff --git a/apps/client/playwright.config.ts b/apps/client/playwright.config.ts new file mode 100644 index 000000000..6ad3a7faa --- /dev/null +++ b/apps/client/playwright.config.ts @@ -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; diff --git a/apps/client/pnpm-lock.yaml b/apps/client/pnpm-lock.yaml new file mode 100644 index 000000000..f9b8429e8 --- /dev/null +++ b/apps/client/pnpm-lock.yaml @@ -0,0 +1,1793 @@ +lockfileVersion: 5.4 + +specifiers: + '@playwright/test': 1.25.0 + '@sveltejs/adapter-auto': next + '@sveltejs/kit': next + '@trpc/client': ^10.1.0 + '@typescript-eslint/eslint-plugin': ^5.27.0 + '@typescript-eslint/parser': ^5.27.0 + eslint: ^8.16.0 + eslint-config-prettier: ^8.3.0 + eslint-plugin-svelte3: ^4.0.0 + prettier: ^2.6.2 + prettier-plugin-svelte: ^2.7.0 + svelte: ^3.44.0 + svelte-check: ^2.7.1 + svelte-preprocess: ^4.10.6 + tslib: ^2.3.1 + typescript: ^4.7.4 + vite: ^3.1.0 + +dependencies: + '@trpc/client': 10.1.0 + +devDependencies: + '@playwright/test': 1.25.0 + '@sveltejs/adapter-auto': 1.0.0-next.89 + '@sveltejs/kit': 1.0.0-next.560_svelte@3.53.1+vite@3.2.4 + '@typescript-eslint/eslint-plugin': 5.44.0_fnsv2sbzcckq65bwfk7a5xwslu + '@typescript-eslint/parser': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + eslint: 8.28.0 + eslint-config-prettier: 8.5.0_eslint@8.28.0 + eslint-plugin-svelte3: 4.0.0_xgu65rlhscpnxffotiaicv6m5i + prettier: 2.8.0 + prettier-plugin-svelte: 2.8.1_3ndnxlh52lolrqe4kgjgbxb3xa + svelte: 3.53.1 + svelte-check: 2.9.2_svelte@3.53.1 + svelte-preprocess: 4.10.7_7dvewpees4iyn2tkw2qzal77a4 + tslib: 2.4.1 + typescript: 4.9.3 + vite: 3.2.4 + +packages: + + /@esbuild/android-arm/0.15.15: + resolution: {integrity: sha512-JJjZjJi2eBL01QJuWjfCdZxcIgot+VoK6Fq7eKF9w4YHm9hwl7nhBR1o2Wnt/WcANk5l9SkpvrldW1PLuXxcbw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64/0.15.15: + resolution: {integrity: sha512-lhz6UNPMDXUhtXSulw8XlFAtSYO26WmHQnCi2Lg2p+/TMiJKNLtZCYUxV4wG6rZMzXmr8InGpNwk+DLT2Hm0PA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@eslint/eslintrc/1.3.3: + resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.4.1 + globals: 13.18.0 + ignore: 5.2.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/config-array/0.11.7: + resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer/1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema/1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jridgewell/resolve-uri/3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec/1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/trace-mapping/0.3.17: + resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@nodelib/fs.scandir/2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.13.0 + dev: true + + /@playwright/test/1.25.0: + resolution: {integrity: sha512-j4EZhTTQI3dBeWblE21EV//swwmBtOpIrLdOIJIRv4uqsLdHgBg1z+JtTg+AeC5o2bAXIE26kDNW5A0TimG8Bg==} + engines: {node: '>=14'} + hasBin: true + dependencies: + '@types/node': 18.11.9 + playwright-core: 1.25.0 + dev: true + + /@polka/url/1.0.0-next.21: + resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} + dev: true + + /@sveltejs/adapter-auto/1.0.0-next.89: + resolution: {integrity: sha512-S+sASFX4sSZD1xEKmZ3zHxQh2DGxXBUpCGAtUakKkI2MRvFIm+zYIm+7GPekofMLd19FjdFDKkuOjBKPdmA8+w==} + dependencies: + import-meta-resolve: 2.2.0 + dev: true + + /@sveltejs/kit/1.0.0-next.560_svelte@3.53.1+vite@3.2.4: + resolution: {integrity: sha512-ldZJyd+jfQWVkOkRHq25cXMffhL5MgB1Uzhhw1ngF8ezB38P/g4T+5ohP8wuk2lxPJIjbY3S6BeXN5mod9XOhA==} + engines: {node: '>=16.14'} + hasBin: true + requiresBuild: true + peerDependencies: + svelte: ^3.44.0 + vite: ^3.2.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 1.3.1_svelte@3.53.1+vite@3.2.4 + '@types/cookie': 0.5.1 + cookie: 0.5.0 + devalue: 4.2.0 + kleur: 4.1.5 + magic-string: 0.26.7 + mime: 3.0.0 + sade: 1.8.1 + set-cookie-parser: 2.5.1 + sirv: 2.0.2 + svelte: 3.53.1 + tiny-glob: 0.2.9 + undici: 5.12.0 + vite: 3.2.4 + transitivePeerDependencies: + - diff-match-patch + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte/1.3.1_svelte@3.53.1+vite@3.2.4: + resolution: {integrity: sha512-2Uu2sDdIR+XQWF7QWOVSF2jR9EU6Ciw1yWfYnfLYj8HIgnNxkh/8g22Fw2pBUI8QNyW/KxtqJUWBI+8ypamSrQ==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + diff-match-patch: ^1.0.5 + svelte: ^3.44.0 + vite: ^3.0.0 + peerDependenciesMeta: + diff-match-patch: + optional: true + dependencies: + debug: 4.3.4 + deepmerge: 4.2.2 + kleur: 4.1.5 + magic-string: 0.26.7 + svelte: 3.53.1 + svelte-hmr: 0.15.1_svelte@3.53.1 + vite: 3.2.4 + vitefu: 0.2.2_vite@3.2.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@trpc/client/10.1.0: + resolution: {integrity: sha512-E7L9l2OTa5lIdM0NYvQLJf/GLapskfiVLv0Jv7t6GVxEOFd+O4THWsWQgJVUUAz9iq805iMNkY3uqSvf4GJaWg==} + peerDependencies: + '@trpc/server': 10.1.0 + dev: false + + /@types/cookie/0.5.1: + resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==} + dev: true + + /@types/json-schema/7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + dev: true + + /@types/node/18.11.9: + resolution: {integrity: sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==} + dev: true + + /@types/pug/2.0.6: + resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} + dev: true + + /@types/sass/1.43.1: + resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} + dependencies: + '@types/node': 18.11.9 + dev: true + + /@types/semver/7.3.13: + resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + dev: true + + /@typescript-eslint/eslint-plugin/5.44.0_fnsv2sbzcckq65bwfk7a5xwslu: + resolution: {integrity: sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/parser': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + '@typescript-eslint/scope-manager': 5.44.0 + '@typescript-eslint/type-utils': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + '@typescript-eslint/utils': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + debug: 4.3.4 + eslint: 8.28.0 + ignore: 5.2.0 + natural-compare-lite: 1.4.0 + regexpp: 3.2.0 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.9.3 + typescript: 4.9.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser/5.44.0_hsf322ms6xhhd4b5ne6lb74y4a: + resolution: {integrity: sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.44.0 + '@typescript-eslint/types': 5.44.0 + '@typescript-eslint/typescript-estree': 5.44.0_typescript@4.9.3 + debug: 4.3.4 + eslint: 8.28.0 + typescript: 4.9.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager/5.44.0: + resolution: {integrity: sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.44.0 + '@typescript-eslint/visitor-keys': 5.44.0 + dev: true + + /@typescript-eslint/type-utils/5.44.0_hsf322ms6xhhd4b5ne6lb74y4a: + resolution: {integrity: sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.44.0_typescript@4.9.3 + '@typescript-eslint/utils': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + debug: 4.3.4 + eslint: 8.28.0 + tsutils: 3.21.0_typescript@4.9.3 + typescript: 4.9.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types/5.44.0: + resolution: {integrity: sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree/5.44.0_typescript@4.9.3: + resolution: {integrity: sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.44.0 + '@typescript-eslint/visitor-keys': 5.44.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.9.3 + typescript: 4.9.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils/5.44.0_hsf322ms6xhhd4b5ne6lb74y4a: + resolution: {integrity: sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.44.0 + '@typescript-eslint/types': 5.44.0 + '@typescript-eslint/typescript-estree': 5.44.0_typescript@4.9.3 + eslint: 8.28.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@8.28.0 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys/5.44.0: + resolution: {integrity: sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.44.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /acorn-jsx/5.3.2_acorn@8.8.1: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.8.1 + dev: true + + /acorn/8.8.1: + resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv/6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex/5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles/4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /anymatch/3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /argparse/2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-union/2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /buffer-crc32/0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + + /busboy/1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: true + + /callsites/3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /chalk/4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chokidar/3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /cookie/0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: true + + /cross-spawn/7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug/4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-is/0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge/4.2.2: + resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} + engines: {node: '>=0.10.0'} + dev: true + + /detect-indent/6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true + + /devalue/4.2.0: + resolution: {integrity: sha512-mbjoAaCL2qogBKgeFxFPOXAUsZchircF+B/79LD4sHH0+NHfYm8gZpQrskKDn5gENGt35+5OI1GUF7hLVnkPDw==} + dev: true + + /dir-glob/3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine/3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /es6-promise/3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + dev: true + + /esbuild-android-64/0.15.15: + resolution: {integrity: sha512-F+WjjQxO+JQOva3tJWNdVjouFMLK6R6i5gjDvgUthLYJnIZJsp1HlF523k73hELY20WPyEO8xcz7aaYBVkeg5Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-android-arm64/0.15.15: + resolution: {integrity: sha512-attlyhD6Y22jNyQ0fIIQ7mnPvDWKw7k6FKnsXlBvQE6s3z6s6cuEHcSgoirquQc7TmZgVCK5fD/2uxmRN+ZpcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-64/0.15.15: + resolution: {integrity: sha512-ohZtF8W1SHJ4JWldsPVdk8st0r9ExbAOSrBOh5L+Mq47i696GVwv1ab/KlmbUoikSTNoXEhDzVpxUR/WIO19FQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-arm64/0.15.15: + resolution: {integrity: sha512-P8jOZ5zshCNIuGn+9KehKs/cq5uIniC+BeCykvdVhx/rBXSxmtj3CUIKZz4sDCuESMbitK54drf/2QX9QHG5Ag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-64/0.15.15: + resolution: {integrity: sha512-KkTg+AmDXz1IvA9S1gt8dE24C8Thx0X5oM0KGF322DuP+P3evwTL9YyusHAWNsh4qLsR80nvBr/EIYs29VSwuA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-arm64/0.15.15: + resolution: {integrity: sha512-FUcML0DRsuyqCMfAC+HoeAqvWxMeq0qXvclZZ/lt2kLU6XBnDA5uKTLUd379WYEyVD4KKFctqWd9tTuk8C/96g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-32/0.15.15: + resolution: {integrity: sha512-q28Qn5pZgHNqug02aTkzw5sW9OklSo96b5nm17Mq0pDXrdTBcQ+M6Q9A1B+dalFeynunwh/pvfrNucjzwDXj+Q==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-64/0.15.15: + resolution: {integrity: sha512-217KPmWMirkf8liO+fj2qrPwbIbhNTGNVtvqI1TnOWJgcMjUWvd677Gq3fTzXEjilkx2yWypVnTswM2KbXgoAg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm/0.15.15: + resolution: {integrity: sha512-RYVW9o2yN8yM7SB1yaWr378CwrjvGCyGybX3SdzPHpikUHkME2AP55Ma20uNwkNyY2eSYFX9D55kDrfQmQBR4w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm64/0.15.15: + resolution: {integrity: sha512-/ltmNFs0FivZkYsTzAsXIfLQX38lFnwJTWCJts0IbCqWZQe+jjj0vYBNbI0kmXLb3y5NljiM5USVAO1NVkdh2g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-mips64le/0.15.15: + resolution: {integrity: sha512-PksEPb321/28GFFxtvL33yVPfnMZihxkEv5zME2zapXGp7fA1X2jYeiTUK+9tJ/EGgcNWuwvtawPxJG7Mmn86A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-ppc64le/0.15.15: + resolution: {integrity: sha512-ek8gJBEIhcpGI327eAZigBOHl58QqrJrYYIZBWQCnH3UnXoeWMrMZLeeZL8BI2XMBhP+sQ6ERctD5X+ajL/AIA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-riscv64/0.15.15: + resolution: {integrity: sha512-H5ilTZb33/GnUBrZMNJtBk7/OXzDHDXjIzoLXHSutwwsLxSNaLxzAaMoDGDd/keZoS+GDBqNVxdCkpuiRW4OSw==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-s390x/0.15.15: + resolution: {integrity: sha512-jKaLUg78mua3rrtrkpv4Or2dNTJU7bgHN4bEjT4OX4GR7nLBSA9dfJezQouTxMmIW7opwEC5/iR9mpC18utnxQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-netbsd-64/0.15.15: + resolution: {integrity: sha512-aOvmF/UkjFuW6F36HbIlImJTTx45KUCHJndtKo+KdP8Dhq3mgLRKW9+6Ircpm8bX/RcS3zZMMmaBLkvGY06Gvw==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-openbsd-64/0.15.15: + resolution: {integrity: sha512-HFFX+WYedx1w2yJ1VyR1Dfo8zyYGQZf1cA69bLdrHzu9svj6KH6ZLK0k3A1/LFPhcEY9idSOhsB2UyU0tHPxgQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-sunos-64/0.15.15: + resolution: {integrity: sha512-jOPBudffG4HN8yJXcK9rib/ZTFoTA5pvIKbRrt3IKAGMq1EpBi4xoVoSRrq/0d4OgZLaQbmkHp8RO9eZIn5atA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-32/0.15.15: + resolution: {integrity: sha512-MDkJ3QkjnCetKF0fKxCyYNBnOq6dmidcwstBVeMtXSgGYTy8XSwBeIE4+HuKiSsG6I/mXEb++px3IGSmTN0XiA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-64/0.15.15: + resolution: {integrity: sha512-xaAUIB2qllE888SsMU3j9nrqyLbkqqkpQyWVkfwSil6BBPgcPk3zOFitTTncEKCLTQy3XV9RuH7PDj3aJDljWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-arm64/0.15.15: + resolution: {integrity: sha512-ttuoCYCIJAFx4UUKKWYnFdrVpoXa3+3WWkXVI6s09U+YjhnyM5h96ewTq/WgQj9LFSIlABQvadHSOQyAVjW5xQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild/0.15.15: + resolution: {integrity: sha512-TEw/lwK4Zzld9x3FedV6jy8onOUHqcEX3ADFk4k+gzPUwrxn8nWV62tH0udo8jOtjFodlEfc4ypsqX3e+WWO6w==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.15.15 + '@esbuild/linux-loong64': 0.15.15 + esbuild-android-64: 0.15.15 + esbuild-android-arm64: 0.15.15 + esbuild-darwin-64: 0.15.15 + esbuild-darwin-arm64: 0.15.15 + esbuild-freebsd-64: 0.15.15 + esbuild-freebsd-arm64: 0.15.15 + esbuild-linux-32: 0.15.15 + esbuild-linux-64: 0.15.15 + esbuild-linux-arm: 0.15.15 + esbuild-linux-arm64: 0.15.15 + esbuild-linux-mips64le: 0.15.15 + esbuild-linux-ppc64le: 0.15.15 + esbuild-linux-riscv64: 0.15.15 + esbuild-linux-s390x: 0.15.15 + esbuild-netbsd-64: 0.15.15 + esbuild-openbsd-64: 0.15.15 + esbuild-sunos-64: 0.15.15 + esbuild-windows-32: 0.15.15 + esbuild-windows-64: 0.15.15 + esbuild-windows-arm64: 0.15.15 + dev: true + + /escape-string-regexp/4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-prettier/8.5.0_eslint@8.28.0: + resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.28.0 + dev: true + + /eslint-plugin-svelte3/4.0.0_xgu65rlhscpnxffotiaicv6m5i: + resolution: {integrity: sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g==} + peerDependencies: + eslint: '>=8.0.0' + svelte: ^3.2.0 + dependencies: + eslint: 8.28.0 + svelte: 3.53.1 + dev: true + + /eslint-scope/5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope/7.1.1: + resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils/3.0.0_eslint@8.28.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.28.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys/2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys/3.3.0: + resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint/8.28.0: + resolution: {integrity: sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.3.3 + '@humanwhocodes/config-array': 0.11.7 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-utils: 3.0.0_eslint@8.28.0 + eslint-visitor-keys: 3.3.0 + espree: 9.4.1 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.18.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.0 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.2.0 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree/9.4.1: + resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.8.1 + acorn-jsx: 5.3.2_acorn@8.8.1 + eslint-visitor-keys: 3.3.0 + dev: true + + /esquery/1.4.0: + resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse/4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse/4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse/5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils/2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal/3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob/3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify/2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein/2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq/1.13.0: + resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache/6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up/5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache/3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flatted/3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /fs.realpath/1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent/6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob/7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals/13.18.0: + resolution: {integrity: sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalyzer/0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + dev: true + + /globby/11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.12 + ignore: 5.2.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /globrex/0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true + + /graceful-fs/4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + dev: true + + /grapheme-splitter/1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /has-flag/4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /ignore/5.2.0: + resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh/3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-meta-resolve/2.2.0: + resolution: {integrity: sha512-CpPOtiCHxP9HdtDM5F45tNiAe66Cqlv3f5uHoJjt+KlaLrUh9/Wz9vepADZ78SlqEo62aDWZtj9ydMGXV+CPnw==} + dev: true + + /imurmurhash/0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-core-module/2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + dependencies: + has: 1.0.3 + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside/3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /isexe/2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-sdsl/4.2.0: + resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==} + dev: true + + /js-yaml/4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-schema-traverse/0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify/1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /kleur/4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true + + /levn/0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /locate-path/6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge/4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /magic-string/0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /magic-string/0.26.7: + resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==} + engines: {node: '>=12'} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime/3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: true + + /min-indent/1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch/3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist/1.2.7: + resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} + dev: true + + /mkdirp/0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.7 + dev: true + + /mri/1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true + + /mrmime/1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + dev: true + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /nanoid/3.3.4: + resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare-lite/1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare/1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /once/1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator/0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /p-limit/3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate/5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module/1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists/4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key/3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type/4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors/1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch/2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /playwright-core/1.25.0: + resolution: {integrity: sha512-kZ3Jwaf3wlu0GgU0nB8UMQ+mXFTqBIFz9h1svTlNduNKjnbPXFxw7mJanLVjqxHJRn62uBfmgBj93YHidk2N5Q==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /postcss/8.4.19: + resolution: {integrity: sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls/1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-plugin-svelte/2.8.1_3ndnxlh52lolrqe4kgjgbxb3xa: + resolution: {integrity: sha512-KA3K1J3/wKDnCxW7ZDRA/QL2Q67N7Xs3gOERqJ5X1qFjq1DdnN3K1R29scSKwh+kA8FF67pXbYytUpvN/i3iQw==} + peerDependencies: + prettier: ^1.16.4 || ^2.0.0 + svelte: ^3.2.0 + dependencies: + prettier: 2.8.0 + svelte: 3.53.1 + dev: true + + /prettier/2.8.0: + resolution: {integrity: sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /punycode/2.1.1: + resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + engines: {node: '>=6'} + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /regexpp/3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /resolve-from/4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf/2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rimraf/3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup/2.79.1: + resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /sade/1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: true + + /sander/0.5.1: + resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} + dependencies: + es6-promise: 3.3.1 + graceful-fs: 4.2.10 + mkdirp: 0.5.6 + rimraf: 2.7.1 + dev: true + + /semver/7.3.8: + resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /set-cookie-parser/2.5.1: + resolution: {integrity: sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==} + dev: true + + /shebang-command/2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex/3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /sirv/2.0.2: + resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.21 + mrmime: 1.0.1 + totalist: 3.0.0 + dev: true + + /slash/3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /sorcery/0.10.0: + resolution: {integrity: sha512-R5ocFmKZQFfSTstfOtHjJuAwbpGyf9qjQa1egyhvXSbM7emjrtLXtGdZsDJDABC85YBfVvrOiGWKSYXPKdvP1g==} + hasBin: true + dependencies: + buffer-crc32: 0.2.13 + minimist: 1.2.7 + sander: 0.5.1 + sourcemap-codec: 1.4.8 + dev: true + + /source-map-js/1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /sourcemap-codec/1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + dev: true + + /streamsearch/1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: true + + /strip-ansi/6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-indent/3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments/3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color/7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /svelte-check/2.9.2_svelte@3.53.1: + resolution: {integrity: sha512-DRi8HhnCiqiGR2YF9ervPGvtoYrheE09cXieCTEqeTPOTJzfoa54Py8rovIBv4bH4n5HgZYIyTQ3DDLHQLl2uQ==} + hasBin: true + peerDependencies: + svelte: ^3.24.0 + dependencies: + '@jridgewell/trace-mapping': 0.3.17 + chokidar: 3.5.3 + fast-glob: 3.2.12 + import-fresh: 3.3.0 + picocolors: 1.0.0 + sade: 1.8.1 + svelte: 3.53.1 + svelte-preprocess: 4.10.7_7dvewpees4iyn2tkw2qzal77a4 + typescript: 4.9.3 + transitivePeerDependencies: + - '@babel/core' + - coffeescript + - less + - node-sass + - postcss + - postcss-load-config + - pug + - sass + - stylus + - sugarss + dev: true + + /svelte-hmr/0.15.1_svelte@3.53.1: + resolution: {integrity: sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: '>=3.19.0' + dependencies: + svelte: 3.53.1 + dev: true + + /svelte-preprocess/4.10.7_7dvewpees4iyn2tkw2qzal77a4: + resolution: {integrity: sha512-sNPBnqYD6FnmdBrUmBCaqS00RyCsCpj2BG58A1JBswNF7b0OKviwxqVrOL/CKyJrLSClrSeqQv5BXNg2RUbPOw==} + engines: {node: '>= 9.11.2'} + requiresBuild: true + peerDependencies: + '@babel/core': ^7.10.2 + coffeescript: ^2.5.1 + less: ^3.11.3 || ^4.0.0 + node-sass: '*' + postcss: ^7 || ^8 + postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 + pug: ^3.0.0 + sass: ^1.26.8 + stylus: ^0.55.0 + sugarss: ^2.0.0 + svelte: ^3.23.0 + typescript: ^3.9.5 || ^4.0.0 + peerDependenciesMeta: + '@babel/core': + optional: true + coffeescript: + optional: true + less: + optional: true + node-sass: + optional: true + postcss: + optional: true + postcss-load-config: + optional: true + pug: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + typescript: + optional: true + dependencies: + '@types/pug': 2.0.6 + '@types/sass': 1.43.1 + detect-indent: 6.1.0 + magic-string: 0.25.9 + sorcery: 0.10.0 + strip-indent: 3.0.0 + svelte: 3.53.1 + typescript: 4.9.3 + dev: true + + /svelte/3.53.1: + resolution: {integrity: sha512-Q4/hHkktZogGhN5iqxqSi9sjEVoe/NbIxX4hXEHoasTxj+TxEQVAq66LnDMdAZxjmsodkoI5F3slqsS68U7FNw==} + engines: {node: '>= 8'} + dev: true + + /text-table/0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /tiny-glob/0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + dev: true + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /totalist/3.0.0: + resolution: {integrity: sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==} + engines: {node: '>=6'} + dev: true + + /tslib/1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib/2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + dev: true + + /tsutils/3.21.0_typescript@4.9.3: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.9.3 + dev: true + + /type-check/0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest/0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typescript/4.9.3: + resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /undici/5.12.0: + resolution: {integrity: sha512-zMLamCG62PGjd9HHMpo05bSLvvwWOZgGeiWlN/vlqu3+lRo3elxktVGEyLMX+IO7c2eflLjcW74AlkhEZm15mg==} + engines: {node: '>=12.18'} + dependencies: + busboy: 1.6.0 + dev: true + + /uri-js/4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.1.1 + dev: true + + /vite/3.2.4: + resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.15.15 + postcss: 8.4.19 + resolve: 1.22.1 + rollup: 2.79.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vitefu/0.2.2_vite@3.2.4: + resolution: {integrity: sha512-8CKEIWPm4B4DUDN+h+hVJa9pyNi7rzc5MYmbxhs1wcMakueGFNWB5/DL30USm9qU3xUPnL4/rrLEAwwFiD1tag==} + peerDependencies: + vite: ^3.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 3.2.4 + dev: true + + /which/2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /word-wrap/1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yocto-queue/0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/apps/client/postcss.config.cjs b/apps/client/postcss.config.cjs new file mode 100644 index 000000000..fe10e55a8 --- /dev/null +++ b/apps/client/postcss.config.cjs @@ -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; diff --git a/apps/client/src/app.d.ts b/apps/client/src/app.d.ts new file mode 100644 index 000000000..8f4d63895 --- /dev/null +++ b/apps/client/src/app.d.ts @@ -0,0 +1,9 @@ +// 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 {} +} diff --git a/apps/client/src/app.html b/apps/client/src/app.html new file mode 100644 index 000000000..0061883f2 --- /dev/null +++ b/apps/client/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/apps/client/src/app.postcss b/apps/client/src/app.postcss new file mode 100644 index 000000000..67ad7e2d9 --- /dev/null +++ b/apps/client/src/app.postcss @@ -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; +} diff --git a/apps/client/src/lib/common.ts b/apps/client/src/lib/common.ts new file mode 100644 index 000000000..ab3b55370 --- /dev/null +++ b/apps/client/src/lib/common.ts @@ -0,0 +1,20 @@ +import { addToast } from './store'; + +export const asyncSleep = (delay: number) => new Promise((resolve) => setTimeout(resolve, delay)); + +export function errorNotification(error: any | { message: string }): void { + if (error instanceof Error) { + addToast({ + message: error.message, + type: 'error' + }); + } else { + addToast({ + message: error, + type: 'error' + }); + } +} +export function getRndInteger(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} diff --git a/apps/client/src/lib/components/Toast.svelte b/apps/client/src/lib/components/Toast.svelte new file mode 100644 index 000000000..bb34929cd --- /dev/null +++ b/apps/client/src/lib/components/Toast.svelte @@ -0,0 +1,64 @@ + + + +
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'} + + {:else if type === 'error'} + + {:else if type === 'info'} + + {/if} + +
diff --git a/apps/client/src/lib/components/Toasts.svelte b/apps/client/src/lib/components/Toasts.svelte new file mode 100644 index 000000000..929189bcb --- /dev/null +++ b/apps/client/src/lib/components/Toasts.svelte @@ -0,0 +1,25 @@ + + +{#if $toasts.length > 0} +
+ +
+{/if} + + diff --git a/apps/client/src/lib/components/Tooltip.svelte b/apps/client/src/lib/components/Tooltip.svelte new file mode 100644 index 000000000..e0591a9d4 --- /dev/null +++ b/apps/client/src/lib/components/Tooltip.svelte @@ -0,0 +1,10 @@ + + + diff --git a/apps/client/src/lib/components/UpdateAvailable.svelte b/apps/client/src/lib/components/UpdateAvailable.svelte new file mode 100644 index 000000000..bda1f02cc --- /dev/null +++ b/apps/client/src/lib/components/UpdateAvailable.svelte @@ -0,0 +1,206 @@ + + +
+ {#if $appSession.teamId === '0'} + {#if $isUpdateAvailable} + + New Version Available! + {/if} + {/if} +
diff --git a/apps/client/src/lib/components/icons/Delete.svelte b/apps/client/src/lib/components/icons/Delete.svelte new file mode 100644 index 000000000..f04b3952c --- /dev/null +++ b/apps/client/src/lib/components/icons/Delete.svelte @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/apps/client/src/lib/components/icons/RemoteLink.svelte b/apps/client/src/lib/components/icons/RemoteLink.svelte new file mode 100644 index 000000000..7622822a2 --- /dev/null +++ b/apps/client/src/lib/components/icons/RemoteLink.svelte @@ -0,0 +1,10 @@ + + + diff --git a/apps/client/src/lib/components/icons/applications/ApplicationIcons.svelte b/apps/client/src/lib/components/icons/applications/ApplicationIcons.svelte new file mode 100644 index 000000000..5e4f83e94 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/ApplicationIcons.svelte @@ -0,0 +1,47 @@ + + +{#if application.buildPack?.toLowerCase() === 'rust'} + +{:else if application.buildPack?.toLowerCase() === 'node'} + +{:else if application.buildPack?.toLowerCase() === 'react'} + +{:else if application.buildPack?.toLowerCase() === 'svelte'} + +{:else if application.buildPack?.toLowerCase() === 'vuejs'} + +{:else if application.buildPack?.toLowerCase() === 'php'} + +{:else if application.buildPack?.toLowerCase() === 'python'} + +{:else if application.buildPack?.toLowerCase() === 'static'} + +{:else if application.buildPack?.toLowerCase() === 'nestjs'} + +{:else if application.buildPack?.toLowerCase() === 'nuxtjs'} + +{:else if application.buildPack?.toLowerCase() === 'nextjs'} + +{:else if application.buildPack?.toLowerCase() === 'gatsby'} + +{:else if application.buildPack?.toLowerCase() === 'docker'} + +{:else if application.buildPack?.toLowerCase() === 'astro'} + +{:else if application.buildPack?.toLowerCase() === 'eleventy'} + +{:else if application.buildPack?.toLowerCase() === 'deno'} + +{:else if application.buildPack?.toLowerCase() === 'laravel'} + +{:else if application.buildPack?.toLowerCase() === 'heroku'} + +{:else if application.buildPack?.toLowerCase() === 'compose'} + +{:else if application.simpleDockerfile} + +{/if} diff --git a/apps/client/src/lib/components/icons/applications/Astro.svelte b/apps/client/src/lib/components/icons/applications/Astro.svelte new file mode 100644 index 000000000..2344372ab --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Astro.svelte @@ -0,0 +1,25 @@ + + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Compose.svelte b/apps/client/src/lib/components/icons/applications/Compose.svelte new file mode 100644 index 000000000..f0482e776 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Compose.svelte @@ -0,0 +1,9 @@ + + +docker compose logo diff --git a/apps/client/src/lib/components/icons/applications/Deno.svelte b/apps/client/src/lib/components/icons/applications/Deno.svelte new file mode 100644 index 000000000..25eee8132 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Deno.svelte @@ -0,0 +1,30 @@ + + + diff --git a/apps/client/src/lib/components/icons/applications/Docker.svelte b/apps/client/src/lib/components/icons/applications/Docker.svelte new file mode 100644 index 000000000..74ba0ebf0 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Docker.svelte @@ -0,0 +1,9 @@ + + + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Eleventy.svelte b/apps/client/src/lib/components/icons/applications/Eleventy.svelte new file mode 100644 index 000000000..b2d8d6122 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Eleventy.svelte @@ -0,0 +1,13 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Gatsby.svelte b/apps/client/src/lib/components/icons/applications/Gatsby.svelte new file mode 100644 index 000000000..d67a63417 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Gatsby.svelte @@ -0,0 +1,13 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Heroku.svelte b/apps/client/src/lib/components/icons/applications/Heroku.svelte new file mode 100644 index 000000000..dff845bc2 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Heroku.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Laravel.svelte b/apps/client/src/lib/components/icons/applications/Laravel.svelte new file mode 100644 index 000000000..d13694a8c --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Laravel.svelte @@ -0,0 +1,14 @@ + + +Logomark diff --git a/apps/client/src/lib/components/icons/applications/Nestjs.svelte b/apps/client/src/lib/components/icons/applications/Nestjs.svelte new file mode 100644 index 000000000..ac0f8af3f --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Nestjs.svelte @@ -0,0 +1,13 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Nextjs.svelte b/apps/client/src/lib/components/icons/applications/Nextjs.svelte new file mode 100644 index 000000000..9ed0227d1 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Nextjs.svelte @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Nodejs.svelte b/apps/client/src/lib/components/icons/applications/Nodejs.svelte new file mode 100644 index 000000000..93140f08f --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Nodejs.svelte @@ -0,0 +1,18 @@ + + + diff --git a/apps/client/src/lib/components/icons/applications/Nuxtjs.svelte b/apps/client/src/lib/components/icons/applications/Nuxtjs.svelte new file mode 100644 index 000000000..cb2a66ff4 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Nuxtjs.svelte @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/apps/client/src/lib/components/icons/applications/PHP.svelte b/apps/client/src/lib/components/icons/applications/PHP.svelte new file mode 100644 index 000000000..d52ab0dd5 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/PHP.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Python.svelte b/apps/client/src/lib/components/icons/applications/Python.svelte new file mode 100644 index 000000000..17abb0f6d --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Python.svelte @@ -0,0 +1,57 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/React.svelte b/apps/client/src/lib/components/icons/applications/React.svelte new file mode 100644 index 000000000..c0867ffc8 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/React.svelte @@ -0,0 +1,16 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Rust.svelte b/apps/client/src/lib/components/icons/applications/Rust.svelte new file mode 100644 index 000000000..97bcee903 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Rust.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Static.svelte b/apps/client/src/lib/components/icons/applications/Static.svelte new file mode 100644 index 000000000..14cbb0ce8 --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Static.svelte @@ -0,0 +1,34 @@ + + + diff --git a/apps/client/src/lib/components/icons/applications/Svelte.svelte b/apps/client/src/lib/components/icons/applications/Svelte.svelte new file mode 100644 index 000000000..cfa96c59d --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Svelte.svelte @@ -0,0 +1,25 @@ + + + + + + diff --git a/apps/client/src/lib/components/icons/applications/Vuejs.svelte b/apps/client/src/lib/components/icons/applications/Vuejs.svelte new file mode 100644 index 000000000..5ead6229d --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/Vuejs.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/applications/index.ts b/apps/client/src/lib/components/icons/applications/index.ts new file mode 100644 index 000000000..7bbe7b55b --- /dev/null +++ b/apps/client/src/lib/components/icons/applications/index.ts @@ -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'; diff --git a/apps/client/src/lib/components/icons/databases/Clickhouse.svelte b/apps/client/src/lib/components/icons/databases/Clickhouse.svelte new file mode 100644 index 000000000..dd237a48c --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/Clickhouse.svelte @@ -0,0 +1,13 @@ + + + diff --git a/apps/client/src/lib/components/icons/databases/CouchDB.svelte b/apps/client/src/lib/components/icons/databases/CouchDB.svelte new file mode 100644 index 000000000..411c4928d --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/CouchDB.svelte @@ -0,0 +1,18 @@ + + + diff --git a/apps/client/src/lib/components/icons/databases/DatabaseIcons.svelte b/apps/client/src/lib/components/icons/databases/DatabaseIcons.svelte new file mode 100644 index 000000000..a9ebf475b --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/DatabaseIcons.svelte @@ -0,0 +1,21 @@ + + +{#if type === 'mysql'} + +{:else if type === 'postgresql'} + +{:else if type === 'mongodb'} + +{:else if type === 'mariadb'} + +{:else if type === 'redis'} + +{:else if type === 'couchdb'} + +{:else if type === 'edgedb'} + +{/if} diff --git a/apps/client/src/lib/components/icons/databases/EdgeDB.svelte b/apps/client/src/lib/components/icons/databases/EdgeDB.svelte new file mode 100644 index 000000000..57fdebed5 --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/EdgeDB.svelte @@ -0,0 +1,22 @@ + + + diff --git a/apps/client/src/lib/components/icons/databases/MariaDB.svelte b/apps/client/src/lib/components/icons/databases/MariaDB.svelte new file mode 100644 index 000000000..5bf504bcc --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/MariaDB.svelte @@ -0,0 +1,17 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/databases/MongoDB.svelte b/apps/client/src/lib/components/icons/databases/MongoDB.svelte new file mode 100644 index 000000000..fbb261aa2 --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/MongoDB.svelte @@ -0,0 +1,90 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/databases/MySQL.svelte b/apps/client/src/lib/components/icons/databases/MySQL.svelte new file mode 100644 index 000000000..095093214 --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/MySQL.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/client/src/lib/components/icons/databases/PostgreSQL.svelte b/apps/client/src/lib/components/icons/databases/PostgreSQL.svelte new file mode 100644 index 000000000..3021508a6 --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/PostgreSQL.svelte @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/client/src/lib/components/icons/databases/Redis.svelte b/apps/client/src/lib/components/icons/databases/Redis.svelte new file mode 100644 index 000000000..24a7dc797 --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/Redis.svelte @@ -0,0 +1,34 @@ + + + diff --git a/apps/client/src/lib/components/icons/databases/index.ts b/apps/client/src/lib/components/icons/databases/index.ts new file mode 100644 index 000000000..e200b5311 --- /dev/null +++ b/apps/client/src/lib/components/icons/databases/index.ts @@ -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'; + + diff --git a/apps/client/src/lib/components/icons/destinations/LocalDocker.svelte b/apps/client/src/lib/components/icons/destinations/LocalDocker.svelte new file mode 100644 index 000000000..f3ab3be56 --- /dev/null +++ b/apps/client/src/lib/components/icons/destinations/LocalDocker.svelte @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/apps/client/src/lib/components/icons/destinations/RemoteDocker.svelte b/apps/client/src/lib/components/icons/destinations/RemoteDocker.svelte new file mode 100644 index 000000000..1d00a6900 --- /dev/null +++ b/apps/client/src/lib/components/icons/destinations/RemoteDocker.svelte @@ -0,0 +1,16 @@ + + + + + + + diff --git a/apps/client/src/lib/components/icons/destinations/index.ts b/apps/client/src/lib/components/icons/destinations/index.ts new file mode 100644 index 000000000..f39255325 --- /dev/null +++ b/apps/client/src/lib/components/icons/destinations/index.ts @@ -0,0 +1,2 @@ +export { default as LocalDocker } from './LocalDocker.svelte'; +export { default as RemoteDocker } from './RemoteDocker.svelte'; diff --git a/apps/client/src/lib/components/icons/index.ts b/apps/client/src/lib/components/icons/index.ts new file mode 100644 index 000000000..7ccdd45c4 --- /dev/null +++ b/apps/client/src/lib/components/icons/index.ts @@ -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'; diff --git a/apps/client/src/lib/components/icons/services/ServiceIcons.svelte b/apps/client/src/lib/components/icons/services/ServiceIcons.svelte new file mode 100644 index 000000000..7f146e3fb --- /dev/null +++ b/apps/client/src/lib/components/icons/services/ServiceIcons.svelte @@ -0,0 +1,59 @@ + + +{#if name} + {`Icon +{/if} diff --git a/apps/client/src/lib/components/icons/sources/Github.svelte b/apps/client/src/lib/components/icons/sources/Github.svelte new file mode 100644 index 000000000..38ef50829 --- /dev/null +++ b/apps/client/src/lib/components/icons/sources/Github.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/sources/Gitlab.svelte b/apps/client/src/lib/components/icons/sources/Gitlab.svelte new file mode 100644 index 000000000..7ddfa21c1 --- /dev/null +++ b/apps/client/src/lib/components/icons/sources/Gitlab.svelte @@ -0,0 +1,25 @@ + + + + + diff --git a/apps/client/src/lib/components/icons/sources/index.ts b/apps/client/src/lib/components/icons/sources/index.ts new file mode 100644 index 000000000..3a0d7cee3 --- /dev/null +++ b/apps/client/src/lib/components/icons/sources/index.ts @@ -0,0 +1,2 @@ +export { default as GitHub } from './Github.svelte'; +export { default as GitLab } from './Gitlab.svelte'; diff --git a/apps/client/src/lib/store.ts b/apps/client/src/lib/store.ts new file mode 100644 index 000000000..8e3687dca --- /dev/null +++ b/apps/client/src/lib/store.ts @@ -0,0 +1,141 @@ +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({ + transformer: superjson, + links: [ + httpBatchLink({ + url: `${serverBaseUrl}/trpc`, + headers() { + return { + Authorization: token + }; + } + }) + ] +}); + +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; +} + +export const appSession: Writable = 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 = writable(false); +export const isUpdateAvailable: Writable = writable(false); +export const latestVersion: Writable = writable('latest'); +export const loginEmail: Writable = writable(); +export const search: any = writable(''); + +export const isDeploymentEnabled: Writable = writable(false); +export const status: Writable = 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 + } +}); diff --git a/apps/client/src/routes/+error.svelte b/apps/client/src/routes/+error.svelte new file mode 100644 index 000000000..eef31e906 --- /dev/null +++ b/apps/client/src/routes/+error.svelte @@ -0,0 +1,14 @@ + + +
+
Ooops, are you lost?
+ Go back + {#if $page.error.message !== 'Not Found'} +
+
{$page
+					.error.message}
+
+ {/if} +
diff --git a/apps/client/src/routes/+layout.svelte b/apps/client/src/routes/+layout.svelte new file mode 100644 index 000000000..cd5ab4cd1 --- /dev/null +++ b/apps/client/src/routes/+layout.svelte @@ -0,0 +1,417 @@ + + + + {#if !$appSession.whiteLabeled} + Coolify + + {:else if $appSession.whiteLabeledDetails.icon} + Coolify + + {/if} + + +
+ +
+ {#if $appSession.userId} + IAM + Settings + Documentation + Logout + + {#if $appSession.whiteLabeled} + Powered by Coolify + {/if} + {/if} + +
+
+ +
+
+
+ +
diff --git a/apps/client/src/routes/+layout.ts b/apps/client/src/routes/+layout.ts new file mode 100644 index 000000000..6a2608cac --- /dev/null +++ b/apps/client/src/routes/+layout.ts @@ -0,0 +1,43 @@ +import { error } from '@sveltejs/kit'; +import { trpc } from '$lib/store'; +import type { LayoutLoad } from './$types'; +import { redirect } from '@sveltejs/kit'; +import Cookies from 'js-cookie'; +export const ssr = false; + +export const load: LayoutLoad = async ({ url }) => { + const { pathname } = new URL(url); + + try { + if (pathname === '/login' || pathname === '/register') { + const baseSettings = await trpc.settings.getBaseSettings.query(); + return { + settings: { + ...baseSettings + } + }; + } + const settings = await trpc.settings.getInstanceSettings.query(); + if (settings.data.token) { + Cookies.set('token', settings.data.token); + } + return { + settings: { + ...settings + } + }; + } catch (err) { + if (err?.data?.httpStatus == 401) { + throw redirect(307, '/login'); + } + if (err instanceof Error) { + throw error(500, { + message: 'An unexpected error occurred, please try again later.' + '

' + err.message + }); + } + + throw error(500, { + message: 'An unexpected error occurred, please try again later.' + }); + } +}; diff --git a/apps/client/src/routes/+page.svelte b/apps/client/src/routes/+page.svelte new file mode 100644 index 000000000..b26c9c4f1 --- /dev/null +++ b/apps/client/src/routes/+page.svelte @@ -0,0 +1,1652 @@ + + + +
+ {#if applications.length !== 0 || destinations.length !== 0 || databases.length !== 0 || services.length !== 0 || gitSources.length !== 0 || destinations.length !== 0} +
+ + + + + +
+
+
+ +
doSearch('')} + > + + + + + +
+ + doSearch()} + /> +
+ +
+ {/if} + {#if (filtered.applications.length > 0 && applications.length > 0) || filtered.otherApplications.length > 0} +
+

Applications

+ + {#if foundUnconfiguredApplication} + + {/if} +
+ {/if} + {#if filtered.applications.length > 0 && applications.length > 0} +
+ + {/if} + {#if filtered.otherApplications.length > 0} + {#if filtered.applications.length > 0} +
+ {/if} + {/if} + {#if filtered.otherApplications.length > 0} + + {/if} + {#if (filtered.services.length > 0 && services.length > 0) || filtered.otherServices.length > 0} +
+

Services

+ + {#if foundUnconfiguredService} + + {/if} +
+ {/if} + {#if filtered.services.length > 0 && services.length > 0} +
+ + {/if} + {#if filtered.otherServices.length > 0} + {#if filtered.services.length > 0} +
+ {/if} + {/if} + {#if filtered.otherServices.length > 0} + + {/if} + {#if (filtered.databases.length > 0 && databases.length > 0) || filtered.otherDatabases.length > 0} +
+

Databases

+ + {#if foundUnconfiguredDatabase} + + {/if} +
+ {/if} + {#if filtered.databases.length > 0 && databases.length > 0} +
+ + {/if} + {#if filtered.otherDatabases.length > 0} + {#if filtered.databases.length > 0} +
+ {/if} + {/if} + {#if filtered.otherDatabases.length > 0} + + {/if} + {#if (filtered.gitSources.length > 0 && gitSources.length > 0) || filtered.otherGitSources.length > 0} +
+

Git Sources

+
+ {/if} + {#if filtered.gitSources.length > 0 && gitSources.length > 0} +
+
+ {#if filtered.gitSources.length > 0} + {#each filtered.gitSources as source} + {#key source.id} + + + {/if} + {#if filtered.otherGitSources.length > 0} + {#if filtered.gitSources.length > 0} +
+ {/if} + {/if} + {#if filtered.otherGitSources.length > 0} +
+ {#each filtered.otherGitSources as source} + {#key source.id} + + + {/if} + {#if (filtered.destinations.length > 0 && destinations.length > 0) || filtered.otherDestinations.length > 0} +
+

Destinations

+
+ {/if} + {#if filtered.destinations.length > 0 && destinations.length > 0} +
+ + {/if} + {#if filtered.otherDestinations.length > 0} + {#if filtered.destinations.length > 0} +
+ {/if} + {/if} + {#if filtered.otherDestinations.length > 0} + + {/if} + + {#if filtered.applications.length === 0 && filtered.destinations.length === 0 && filtered.databases.length === 0 && filtered.services.length === 0 && filtered.gitSources.length === 0 && filtered.destinations.length === 0 && $search} +
+

+ Nothing found with {$search}. +

+
+ {/if} + {#if applications.length === 0 && destinations.length === 0 && databases.length === 0 && services.length === 0 && gitSources.length === 0 && destinations.length === 0} +
+
+
+

+ Hey +

+

It looks like you did not configure anything yet.

+ +
+
+
+ {/if} +
+
diff --git a/apps/client/src/routes/+page.ts b/apps/client/src/routes/+page.ts new file mode 100644 index 000000000..4652e3d53 --- /dev/null +++ b/apps/client/src/routes/+page.ts @@ -0,0 +1,16 @@ +import { error } from '@sveltejs/kit'; +import { trpc } from '$lib/store'; +import type { LayoutLoad } from './$types'; +import { redirect } from '@sveltejs/kit'; +import Cookies from 'js-cookie'; +export const ssr = false; + +export const load: LayoutLoad = async ({ url }) => { + try { + return await trpc.dashboard.resources.query(); + } catch (err) { + throw error(500, { + message: 'An unexpected error occurred, please try again later.' + }); + } +}; diff --git a/apps/client/src/routes/_components/NewResource.svelte b/apps/client/src/routes/_components/NewResource.svelte new file mode 100644 index 000000000..b540f3d12 --- /dev/null +++ b/apps/client/src/routes/_components/NewResource.svelte @@ -0,0 +1,155 @@ + + + + diff --git a/apps/client/src/routes/applications/[id]/+layout.svelte b/apps/client/src/routes/applications/[id]/+layout.svelte new file mode 100644 index 000000000..89e214266 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/+layout.svelte @@ -0,0 +1,114 @@ + + +
+ +
+ {#if $status.application.initialLoading} + + {:else if $status.application.overallStatus === 'degraded'} + (stopping = true)} + on:stopped={() => (stopping = false)} + /> + {:else if $status.application.overallStatus === 'healthy'} + + {:else if $status.application.overallStatus === 'stopped'} + + {/if} +
+
+
+ {#if !isConfigurationView} + + {/if} +
+ +
+
diff --git a/apps/client/src/routes/applications/[id]/+layout.ts b/apps/client/src/routes/applications/[id]/+layout.ts new file mode 100644 index 000000000..3bbc66f62 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/+layout.ts @@ -0,0 +1,56 @@ +import { error } from '@sveltejs/kit'; +import { trpc } from '$lib/store'; +import type { LayoutLoad } from './$types'; +import { redirect } from '@sveltejs/kit'; + +function checkConfiguration(application: any): string | null { + let configurationPhase = null; + if (!application.gitSourceId && !application.simpleDockerfile) { + return (configurationPhase = 'source'); + } + if (application.simpleDockerfile) { + if (!application.destinationDockerId) { + configurationPhase = 'destination'; + } + return configurationPhase; + } else if (!application.repository && !application.branch) { + configurationPhase = 'repository'; + } else if (!application.destinationDockerId) { + configurationPhase = 'destination'; + } else if (!application.buildPack) { + configurationPhase = 'buildpack'; + } + return configurationPhase; +} + +export const load: LayoutLoad = async ({ params, url }) => { + const { pathname } = new URL(url); + const { id } = params; + try { + const application = await trpc.applications.getApplicationById.query({ id }); + if (!application) { + throw redirect(307, '/applications'); + } + const configurationPhase = checkConfiguration(application); + console.log({ configurationPhase }); + // if ( + // configurationPhase && + // pathname !== `/applications/${params.id}/configuration/${configurationPhase}` + // ) { + // throw redirect(302, `/applications/${params.id}/configuration/${configurationPhase}`); + // } + return { + application + }; + } catch (err) { + if (err instanceof Error) { + throw error(500, { + message: 'An unexpected error occurred, please try again later.' + '

' + err.message + }); + } + + throw error(500, { + message: 'An unexpected error occurred, please try again later.' + }); + } +}; diff --git a/apps/client/src/routes/applications/[id]/+page.svelte b/apps/client/src/routes/applications/[id]/+page.svelte new file mode 100644 index 000000000..e69de29bb diff --git a/apps/client/src/routes/applications/[id]/_components/Buttons/Delete.svelte b/apps/client/src/routes/applications/[id]/_components/Buttons/Delete.svelte new file mode 100644 index 000000000..182419e03 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/Buttons/Delete.svelte @@ -0,0 +1,29 @@ + + + diff --git a/apps/client/src/routes/applications/[id]/_components/Buttons/Deploy.svelte b/apps/client/src/routes/applications/[id]/_components/Buttons/Deploy.svelte new file mode 100644 index 000000000..0a934440f --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/Buttons/Deploy.svelte @@ -0,0 +1,31 @@ + + + diff --git a/apps/client/src/routes/applications/[id]/_components/Buttons/ForceDeploy.svelte b/apps/client/src/routes/applications/[id]/_components/Buttons/ForceDeploy.svelte new file mode 100644 index 000000000..53a08a0e8 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/Buttons/ForceDeploy.svelte @@ -0,0 +1,34 @@ + + + diff --git a/apps/client/src/routes/applications/[id]/_components/Buttons/Loading.svelte b/apps/client/src/routes/applications/[id]/_components/Buttons/Loading.svelte new file mode 100644 index 000000000..30336d843 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/Buttons/Loading.svelte @@ -0,0 +1,21 @@ + diff --git a/apps/client/src/routes/applications/[id]/_components/Buttons/Restart.svelte b/apps/client/src/routes/applications/[id]/_components/Buttons/Restart.svelte new file mode 100644 index 000000000..d9f416745 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/Buttons/Restart.svelte @@ -0,0 +1,30 @@ + + + diff --git a/apps/client/src/routes/applications/[id]/_components/Buttons/Stop.svelte b/apps/client/src/routes/applications/[id]/_components/Buttons/Stop.svelte new file mode 100644 index 000000000..e9925d08e --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/Buttons/Stop.svelte @@ -0,0 +1,37 @@ + + + diff --git a/apps/client/src/routes/applications/[id]/_components/Buttons/index.ts b/apps/client/src/routes/applications/[id]/_components/Buttons/index.ts new file mode 100644 index 000000000..8e6e8a7f0 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/Buttons/index.ts @@ -0,0 +1,6 @@ +export { default as Delete } from './Delete.svelte'; +export { default as Stop } from './Stop.svelte'; +export { default as Restart } from './Restart.svelte'; +export { default as Deploy } from './Deploy.svelte'; +export { default as ForceDeploy } from './ForceDeploy.svelte'; +export { default as Loading } from './Loading.svelte'; diff --git a/apps/client/src/routes/applications/[id]/_components/Menu.svelte b/apps/client/src/routes/applications/[id]/_components/Menu.svelte new file mode 100644 index 000000000..4c7ddb76c --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/Menu.svelte @@ -0,0 +1,278 @@ + + + diff --git a/apps/client/src/routes/applications/[id]/_components/States/Degraded.svelte b/apps/client/src/routes/applications/[id]/_components/States/Degraded.svelte new file mode 100644 index 000000000..6f0a1ff06 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/States/Degraded.svelte @@ -0,0 +1,26 @@ + + + + + + + + + + Application Error (check logs) + + diff --git a/apps/client/src/routes/applications/[id]/_components/States/Healthy.svelte b/apps/client/src/routes/applications/[id]/_components/States/Healthy.svelte new file mode 100644 index 000000000..3b1cf1cf1 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/States/Healthy.svelte @@ -0,0 +1,11 @@ + + +{#if !isComposeBuildPack} + +{/if} + + diff --git a/apps/client/src/routes/applications/[id]/_components/States/Loading.svelte b/apps/client/src/routes/applications/[id]/_components/States/Loading.svelte new file mode 100644 index 000000000..bc77579c5 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/States/Loading.svelte @@ -0,0 +1,5 @@ + + + diff --git a/apps/client/src/routes/applications/[id]/_components/States/Stopped.svelte b/apps/client/src/routes/applications/[id]/_components/States/Stopped.svelte new file mode 100644 index 000000000..e5817b490 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/States/Stopped.svelte @@ -0,0 +1,6 @@ + + + diff --git a/apps/client/src/routes/applications/[id]/_components/States/index.ts b/apps/client/src/routes/applications/[id]/_components/States/index.ts new file mode 100644 index 000000000..a374936d0 --- /dev/null +++ b/apps/client/src/routes/applications/[id]/_components/States/index.ts @@ -0,0 +1,4 @@ +export { default as Loading } from './Loading.svelte'; +export { default as Degraded } from './Degraded.svelte'; +export { default as Healthy } from './Healthy.svelte'; +export { default as Stopped } from './Stopped.svelte'; diff --git a/apps/client/src/routes/applications/[id]/utils.ts b/apps/client/src/routes/applications/[id]/utils.ts new file mode 100644 index 000000000..4464f932d --- /dev/null +++ b/apps/client/src/routes/applications/[id]/utils.ts @@ -0,0 +1,7 @@ +import { goto } from '$app/navigation'; +import { errorNotification } from '$lib/common'; +import { trpc } from '$lib/store'; + +export async function saveForm() { + return await trpc.applications.save.mutate(); +} diff --git a/apps/client/src/routes/login/+page.svelte b/apps/client/src/routes/login/+page.svelte new file mode 100644 index 000000000..07cbd17d6 --- /dev/null +++ b/apps/client/src/routes/login/+page.svelte @@ -0,0 +1,123 @@ + + + + Login + + +
+ +
+
+ {#if $appSession.whiteLabeledDetails.icon} +
+ Icon for white labeled version of Coolify +
+ {:else} +
+
+ Coolify icon +
+
+
+

Coolify

+
+ {/if} +
+
+
+

Welcome back

+
Please login to continue.
+
+
+ + + +
+ + {#if $appSession.isRegistrationEnabled} + + {:else} +
+ Registration is disabled. Please ask an admin to activate it. +
+ {/if} +
+
+ {#if browser && window.location.host === 'demo.coolify.io'} +
+ Registration is open, just fill in an email (does not + need to be live email address for the demo instance) and a password. +
+
+ All users gets an own namespace, so you won't be able + to access other users data. +
+ {/if} +
+
+
diff --git a/apps/client/src/routes/register/+page.svelte b/apps/client/src/routes/register/+page.svelte new file mode 100644 index 000000000..37603dc1d --- /dev/null +++ b/apps/client/src/routes/register/+page.svelte @@ -0,0 +1,146 @@ + + +
+ +
+
+ +
goto('/')}> + + + + + + +
+
+ {#if $appSession.whiteLabeledDetails.icon} +
+ Icon for white labeled version of Coolify +
+ {:else} +
+
+ Coolify icon +
+
+
+

Coolify

+
+ {/if} +
+
+
+
+

Get started

+
Enter the required fields to complete the registration.
+
+
+ + + + +
+ +
+
+ {#if userCount === 0} +
"First user will be admin."
+ {/if} +
+
+
diff --git a/apps/client/static/favicon.png b/apps/client/static/favicon.png new file mode 100644 index 000000000..2c4482801 Binary files /dev/null and b/apps/client/static/favicon.png differ diff --git a/apps/client/static/icons/appsmith.png b/apps/client/static/icons/appsmith.png new file mode 100644 index 000000000..314ad9053 Binary files /dev/null and b/apps/client/static/icons/appsmith.png differ diff --git a/apps/client/static/icons/appwrite.png b/apps/client/static/icons/appwrite.png new file mode 100644 index 000000000..2aaa3e1ab Binary files /dev/null and b/apps/client/static/icons/appwrite.png differ diff --git a/apps/client/static/icons/compose.png b/apps/client/static/icons/compose.png new file mode 100644 index 000000000..b9cfb40e1 Binary files /dev/null and b/apps/client/static/icons/compose.png differ diff --git a/apps/client/static/icons/default.png b/apps/client/static/icons/default.png new file mode 100644 index 000000000..2c4482801 Binary files /dev/null and b/apps/client/static/icons/default.png differ diff --git a/apps/client/static/icons/fider.png b/apps/client/static/icons/fider.png new file mode 100644 index 000000000..6e34acafe Binary files /dev/null and b/apps/client/static/icons/fider.png differ diff --git a/apps/client/static/icons/ghost.png b/apps/client/static/icons/ghost.png new file mode 100644 index 000000000..f4e300654 Binary files /dev/null and b/apps/client/static/icons/ghost.png differ diff --git a/apps/client/static/icons/gitea.svg b/apps/client/static/icons/gitea.svg new file mode 100644 index 000000000..87c7c82db --- /dev/null +++ b/apps/client/static/icons/gitea.svg @@ -0,0 +1 @@ + diff --git a/apps/client/static/icons/glitchtip.svg b/apps/client/static/icons/glitchtip.svg new file mode 100644 index 000000000..2ae389aae --- /dev/null +++ b/apps/client/static/icons/glitchtip.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/client/static/icons/grafana.png b/apps/client/static/icons/grafana.png new file mode 100644 index 000000000..c8659494f Binary files /dev/null and b/apps/client/static/icons/grafana.png differ diff --git a/apps/client/static/icons/hasura.png b/apps/client/static/icons/hasura.png new file mode 100644 index 000000000..30490401e Binary files /dev/null and b/apps/client/static/icons/hasura.png differ diff --git a/apps/client/static/icons/keycloak.png b/apps/client/static/icons/keycloak.png new file mode 100644 index 000000000..8f29f7af7 Binary files /dev/null and b/apps/client/static/icons/keycloak.png differ diff --git a/apps/client/static/icons/languagetool.svg b/apps/client/static/icons/languagetool.svg new file mode 100644 index 000000000..fc8cab02f --- /dev/null +++ b/apps/client/static/icons/languagetool.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/apps/client/static/icons/lavalink.png b/apps/client/static/icons/lavalink.png new file mode 100644 index 000000000..114458bf2 Binary files /dev/null and b/apps/client/static/icons/lavalink.png differ diff --git a/apps/client/static/icons/meilisearch.svg b/apps/client/static/icons/meilisearch.svg new file mode 100644 index 000000000..72a8f30e1 --- /dev/null +++ b/apps/client/static/icons/meilisearch.svg @@ -0,0 +1,40 @@ + diff --git a/apps/client/static/icons/minio.png b/apps/client/static/icons/minio.png new file mode 100644 index 000000000..d1b32be9e Binary files /dev/null and b/apps/client/static/icons/minio.png differ diff --git a/apps/client/static/icons/moodle.png b/apps/client/static/icons/moodle.png new file mode 100644 index 000000000..a5cec5be7 Binary files /dev/null and b/apps/client/static/icons/moodle.png differ diff --git a/apps/client/static/icons/n8n.svg b/apps/client/static/icons/n8n.svg new file mode 100644 index 000000000..262a6e7a6 --- /dev/null +++ b/apps/client/static/icons/n8n.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/client/static/icons/nocodb.png b/apps/client/static/icons/nocodb.png new file mode 100644 index 000000000..dec31a6dd Binary files /dev/null and b/apps/client/static/icons/nocodb.png differ diff --git a/apps/client/static/icons/plausibleanalytics.png b/apps/client/static/icons/plausibleanalytics.png new file mode 100644 index 000000000..c02e68d63 Binary files /dev/null and b/apps/client/static/icons/plausibleanalytics.png differ diff --git a/apps/client/static/icons/pocketbase.svg b/apps/client/static/icons/pocketbase.svg new file mode 100644 index 000000000..5b5de956b --- /dev/null +++ b/apps/client/static/icons/pocketbase.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/client/static/icons/searxng.svg b/apps/client/static/icons/searxng.svg new file mode 100644 index 000000000..c0dd11b43 --- /dev/null +++ b/apps/client/static/icons/searxng.svg @@ -0,0 +1,56 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/apps/client/static/icons/trilium.png b/apps/client/static/icons/trilium.png new file mode 100644 index 000000000..249aceef3 Binary files /dev/null and b/apps/client/static/icons/trilium.png differ diff --git a/apps/client/static/icons/umami.svg b/apps/client/static/icons/umami.svg new file mode 100644 index 000000000..b9d1abca3 --- /dev/null +++ b/apps/client/static/icons/umami.svg @@ -0,0 +1,78 @@ + + Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + diff --git a/apps/client/static/icons/uptimekuma.svg b/apps/client/static/icons/uptimekuma.svg new file mode 100644 index 000000000..e67bc836a --- /dev/null +++ b/apps/client/static/icons/uptimekuma.svg @@ -0,0 +1,154 @@ + diff --git a/apps/client/static/icons/vaultwarden.svg b/apps/client/static/icons/vaultwarden.svg new file mode 100644 index 000000000..15433d8ed --- /dev/null +++ b/apps/client/static/icons/vaultwarden.svg @@ -0,0 +1,30 @@ + + + + + diff --git a/apps/client/static/icons/vscodeserver.png b/apps/client/static/icons/vscodeserver.png new file mode 100644 index 000000000..37a084767 Binary files /dev/null and b/apps/client/static/icons/vscodeserver.png differ diff --git a/apps/client/static/icons/weblate.svg b/apps/client/static/icons/weblate.svg new file mode 100644 index 000000000..99c6ac483 --- /dev/null +++ b/apps/client/static/icons/weblate.svg @@ -0,0 +1,56 @@ + diff --git a/apps/client/static/icons/wordpress.svg b/apps/client/static/icons/wordpress.svg new file mode 100644 index 000000000..31149e2ba --- /dev/null +++ b/apps/client/static/icons/wordpress.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/client/static/poppins-v19-latin-ext_latin_devanagari-500.woff b/apps/client/static/poppins-v19-latin-ext_latin_devanagari-500.woff new file mode 100644 index 000000000..d5e713b46 Binary files /dev/null and b/apps/client/static/poppins-v19-latin-ext_latin_devanagari-500.woff differ diff --git a/apps/client/static/poppins-v19-latin-ext_latin_devanagari-500.woff2 b/apps/client/static/poppins-v19-latin-ext_latin_devanagari-500.woff2 new file mode 100644 index 000000000..6e1053fbc Binary files /dev/null and b/apps/client/static/poppins-v19-latin-ext_latin_devanagari-500.woff2 differ diff --git a/apps/client/static/poppins-v19-latin-ext_latin_devanagari-regular.woff b/apps/client/static/poppins-v19-latin-ext_latin_devanagari-regular.woff new file mode 100644 index 000000000..ccce523fe Binary files /dev/null and b/apps/client/static/poppins-v19-latin-ext_latin_devanagari-regular.woff differ diff --git a/apps/client/static/poppins-v19-latin-ext_latin_devanagari-regular.woff2 b/apps/client/static/poppins-v19-latin-ext_latin_devanagari-regular.woff2 new file mode 100644 index 000000000..7457692a2 Binary files /dev/null and b/apps/client/static/poppins-v19-latin-ext_latin_devanagari-regular.woff2 differ diff --git a/apps/client/svelte.config.js b/apps/client/svelte.config.js new file mode 100644 index 000000000..b7195b469 --- /dev/null +++ b/apps/client/svelte.config.js @@ -0,0 +1,21 @@ +import adapter from '@sveltejs/adapter-static'; +import preprocess from 'svelte-preprocess'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: [ + preprocess({ + postcss: true + }) + ], + kit: { + adapter: adapter({ + pages: 'build', + assets: 'build', + fallback: 'index.html', + precompress: true + }) + } +}; + +export default config; diff --git a/apps/client/tailwind.config.cjs b/apps/client/tailwind.config.cjs new file mode 100644 index 000000000..cab4ddbb7 --- /dev/null +++ b/apps/client/tailwind.config.cjs @@ -0,0 +1,76 @@ +const defaultTheme = require('tailwindcss/defaultTheme'); + +module.exports = { + content: [ + './**/*.html', + './src/**/*.{js,jsx,ts,tsx,svelte}', + './node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}' + ], + important: true, + daisyui: { + themes: [ + { + coollabs: { + 'base-100': '#323232', + 'base-200': '#242424', + 'base-300': '#181818', + primary: '#6B16ED', + 'primary-content': '#fff', + secondary: '#343232', + accent: '#343232', + neutral: '#272626', + info: '#0284c7', + success: '#16A34A', + warning: '#FFFF00', + error: '#DC2626', + '--rounded-btn': '0.3rem', + '--btn-text-case': 'normal' + } + } + ] + }, + theme: { + extend: { + keyframes: { + wiggle: { + '0%, 100%': { transform: 'rotate(-3deg)' }, + '50%': { transform: 'rotate(3deg)' } + } + }, + animation: { + wiggle: 'wiggle 0.5s ease-in-out infinite' + }, + fontFamily: { + sans: ['Poppins', ...defaultTheme.fontFamily.sans] + }, + colors: { + applications: '#16A34A', + databases: '#9333EA', + 'databases-100': '#9b46ea', + destinations: '#0284C7', + sources: '#EA580C', + services: '#DB2777', + settings: '#FEE440', + iam: '#C026D3', + coollabs: '#6B16ED', + 'coollabs-100': '#7317FF', + coolblack: '#141414', + 'coolgray-100': '#181818', + 'coolgray-200': '#202020', + 'coolgray-300': '#242424', + 'coolgray-400': '#282828', + 'coolgray-500': '#323232' + } + } + }, + variants: { + scrollbar: ['dark'], + extend: {} + }, + darkMode: 'class', + plugins: [ + require('tailwindcss-scrollbar'), + require('daisyui'), + require('@tailwindcss/typography') + ] +}; diff --git a/apps/client/tests/test.ts b/apps/client/tests/test.ts new file mode 100644 index 000000000..4e579377e --- /dev/null +++ b/apps/client/tests/test.ts @@ -0,0 +1,6 @@ +import { expect, test } from '@playwright/test'; + +test('index page has expected h1', async ({ page }) => { + await page.goto('/'); + expect(await page.textContent('h1')).toBe('Welcome to SvelteKit'); +}); diff --git a/apps/client/tsconfig.json b/apps/client/tsconfig.json new file mode 100644 index 000000000..dc270e7f0 --- /dev/null +++ b/apps/client/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "exclude": ["node_modules/*", ".svelte-kit/*", "public/*"], + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": false, + "paths": { + "$lib": ["src/lib"], + "$lib/*": ["src/lib/*"] + } + } +} diff --git a/apps/client/vite.config.ts b/apps/client/vite.config.ts new file mode 100644 index 000000000..65a557e19 --- /dev/null +++ b/apps/client/vite.config.ts @@ -0,0 +1,11 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import type { UserConfig } from 'vite'; + +const config: UserConfig = { + server: { + host: '0.0.0.0', + }, + plugins: [sveltekit()] +}; + +export default config; diff --git a/apps/server/.env-template b/apps/server/.env-template new file mode 100644 index 000000000..d038e068c --- /dev/null +++ b/apps/server/.env-template @@ -0,0 +1,4 @@ +NODE_ENV="development" + +COOLIFY_DATABASE_URL="file:../db/dev.db" +COOLIFY_SECRET_KEY="32-character-long-secret-key" \ No newline at end of file diff --git a/apps/server/.prettierrc b/apps/server/.prettierrc new file mode 100644 index 000000000..a77fddea9 --- /dev/null +++ b/apps/server/.prettierrc @@ -0,0 +1,9 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte"], + "pluginSearchDirs": ["."], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] +} diff --git a/apps/server/db/.gitkeep b/apps/server/db/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/server/package.json b/apps/server/package.json new file mode 100644 index 000000000..e7f2e15c2 --- /dev/null +++ b/apps/server/package.json @@ -0,0 +1,71 @@ +{ + "name": "server", + "description": "Coolify's Fastify API", + "license": "Apache-2.0", + "scripts": { + "build": "rimraf ../../build && tsc --outDir ../../build", + "dev": "tsx watch --clear-screen=false src", + "lint": "prettier --plugin-search-dir . --check . && eslint .", + "format": "prettier --plugin-search-dir . --write .", + "test-dev": "start-server-and-test 'tsx src/server' http-get://localhost:2022 'tsx src/client'", + "test-start": "start-server-and-test 'node dist/server' http-get://localhost:2022 'node dist/client'", + "db:generate": "prisma generate", + "db:push": "prisma db push && prisma generate", + "db:seed": "prisma db seed", + "db:studio": "prisma studio", + "db:migrate": "DATABASE_URL=file:../db/migration.db prisma migrate dev --skip-seed --name" + }, + "dependencies": { + "@fastify/autoload": "5.6.0", + "@fastify/cors": "8.2.0", + "@fastify/env": "4.1.0", + "@fastify/jwt": "6.5.0", + "@fastify/static": "6.6.0", + "@fastify/websocket": "7.1.1", + "@prisma/client": "4.6.1", + "@trpc/client": "10.1.0", + "@trpc/server": "10.1.0", + "abort-controller": "3.0.0", + "bcryptjs": "2.4.3", + "cuid": "2.1.8", + "dayjs": "1.11.6", + "dotenv": "^16.0.3", + "execa": "6.1.0", + "fastify": "4.10.2", + "fastify-plugin": "4.4.0", + "got": "^12.5.3", + "is-port-reachable": "4.0.0", + "js-yaml": "4.1.0", + "jsonwebtoken": "8.5.1", + "node-fetch": "3.3.0", + "prisma": "4.6.1", + "shell-quote": "^1.7.4", + "ssh-config": "4.1.6", + "superjson": "1.11.0", + "tslib": "2.4.1", + "unique-names-generator": "4.7.1", + "ws": "8.11.0", + "zod": "3.19.1" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.2", + "@types/js-yaml": "^4.0.5", + "@types/jsonwebtoken": "^8.5.9", + "@types/node": "18.11.9", + "@types/node-fetch": "2.6.2", + "@types/shell-quote": "^1.7.1", + "@types/ws": "8.5.3", + "npm-run-all": "4.1.5", + "rimraf": "3.0.2", + "start-server-and-test": "1.14.0", + "tsx": "3.12.1", + "typescript": "4.9.3", + "wait-port": "1.0.4" + }, + "publishConfig": { + "access": "restricted" + }, + "prisma": { + "seed": "tsx prisma/seed.ts" + } +} diff --git a/apps/server/prisma/migrations/20220131142425_init/migration.sql b/apps/server/prisma/migrations/20220131142425_init/migration.sql new file mode 100644 index 000000000..6607a48b5 --- /dev/null +++ b/apps/server/prisma/migrations/20220131142425_init/migration.sql @@ -0,0 +1,443 @@ +-- CreateTable +CREATE TABLE "Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL PRIMARY KEY, + "email" TEXT NOT NULL, + "type" TEXT NOT NULL, + "password" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "Permission" ( + "id" TEXT NOT NULL PRIMARY KEY, + "userId" TEXT NOT NULL, + "teamId" TEXT NOT NULL, + "permission" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Permission_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "Permission_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Team" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "databaseId" TEXT, + "serviceId" TEXT, + FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "TeamInvitation" ( + "id" TEXT NOT NULL PRIMARY KEY, + "uid" TEXT NOT NULL, + "email" TEXT NOT NULL, + "teamId" TEXT NOT NULL, + "teamName" TEXT NOT NULL, + "permission" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- CreateTable +CREATE TABLE "Application" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "fqdn" TEXT, + "repository" TEXT, + "configHash" TEXT, + "branch" TEXT, + "buildPack" TEXT, + "projectId" INTEGER, + "port" INTEGER, + "installCommand" TEXT, + "buildCommand" TEXT, + "startCommand" TEXT, + "baseDirectory" TEXT, + "publishDirectory" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "destinationDockerId" TEXT, + "gitSourceId" TEXT, + CONSTRAINT "Application_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "Application_gitSourceId_fkey" FOREIGN KEY ("gitSourceId") REFERENCES "GitSource" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "ApplicationSettings" ( + "id" TEXT NOT NULL PRIMARY KEY, + "applicationId" TEXT NOT NULL, + "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 +); + +-- CreateTable +CREATE TABLE "Secret" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "value" TEXT NOT NULL, + "isBuildSecret" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "applicationId" TEXT NOT NULL, + CONSTRAINT "Secret_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "BuildLog" ( + "id" TEXT NOT NULL PRIMARY KEY, + "applicationId" TEXT, + "buildId" TEXT NOT NULL, + "line" TEXT NOT NULL, + "time" INTEGER NOT NULL +); + +-- CreateTable +CREATE TABLE "Build" ( + "id" TEXT NOT NULL PRIMARY KEY, + "type" TEXT NOT NULL, + "applicationId" TEXT, + "destinationDockerId" TEXT, + "gitSourceId" TEXT, + "githubAppId" TEXT, + "gitlabAppId" TEXT, + "commit" TEXT, + "status" TEXT DEFAULT 'queued', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "DestinationDocker" ( + "id" TEXT NOT NULL PRIMARY KEY, + "network" TEXT NOT NULL, + "name" TEXT NOT NULL, + "engine" TEXT NOT NULL, + "remoteEngine" BOOLEAN NOT NULL DEFAULT false, + "isCoolifyProxyUsed" BOOLEAN DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "GitSource" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "type" TEXT, + "apiUrl" TEXT, + "htmlUrl" TEXT, + "organization" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "githubAppId" TEXT, + "gitlabAppId" TEXT, + CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "GithubApp" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT, + "appId" INTEGER, + "installationId" INTEGER, + "clientId" TEXT, + "clientSecret" TEXT, + "webhookSecret" TEXT, + "privateKey" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "GitlabApp" ( + "id" TEXT NOT NULL PRIMARY KEY, + "oauthId" INTEGER NOT NULL, + "groupName" TEXT, + "deployKeyId" INTEGER, + "privateSshKey" TEXT, + "publicSshKey" TEXT, + "webhookToken" TEXT, + "appId" TEXT, + "appSecret" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "Database" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "publicPort" INTEGER, + "defaultDatabase" TEXT, + "type" TEXT, + "version" TEXT, + "dbUser" TEXT, + "dbUserPassword" TEXT, + "rootUser" TEXT, + "rootUserPassword" TEXT, + "destinationDockerId" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Database_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "DatabaseSettings" ( + "id" TEXT NOT NULL PRIMARY KEY, + "databaseId" TEXT NOT NULL, + "isPublic" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "DatabaseSettings_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Service" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "fqdn" TEXT, + "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 +); + +-- CreateTable +CREATE TABLE "PlausibleAnalytics" ( + "id" TEXT NOT NULL PRIMARY KEY, + "email" TEXT, + "username" TEXT, + "password" TEXT NOT NULL, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "secretKeyBase" TEXT, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "PlausibleAnalytics_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Minio" ( + "id" TEXT NOT NULL PRIMARY KEY, + "rootUser" TEXT NOT NULL, + "rootUserPassword" TEXT NOT NULL, + "publicPort" INTEGER, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Minio_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Vscodeserver" ( + "id" TEXT NOT NULL PRIMARY KEY, + "password" TEXT NOT NULL, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Vscodeserver_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "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, + "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 +); + +-- CreateTable +CREATE TABLE "_TeamToUser" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + FOREIGN KEY ("A") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("B") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "_ApplicationToTeam" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + FOREIGN KEY ("A") REFERENCES "Application" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "_GitSourceToTeam" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + FOREIGN KEY ("A") REFERENCES "GitSource" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "_GithubAppToTeam" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + FOREIGN KEY ("A") REFERENCES "GithubApp" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "_GitlabAppToTeam" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + FOREIGN KEY ("A") REFERENCES "GitlabApp" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "_DestinationDockerToTeam" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + FOREIGN KEY ("A") REFERENCES "DestinationDocker" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "_DatabaseToTeam" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + FOREIGN KEY ("A") REFERENCES "Database" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "_ServiceToTeam" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + FOREIGN KEY ("A") REFERENCES "Service" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_id_key" ON "User"("id"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Application_fqdn_key" ON "Application"("fqdn"); + +-- CreateIndex +CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Secret_name_key" ON "Secret"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "DestinationDocker_network_key" ON "DestinationDocker"("network"); + +-- CreateIndex +CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GithubApp_name_key" ON "GithubApp"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "GitlabApp_oauthId_key" ON "GitlabApp"("oauthId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GitlabApp_groupName_key" ON "GitlabApp"("groupName"); + +-- CreateIndex +CREATE UNIQUE INDEX "DatabaseSettings_databaseId_key" ON "DatabaseSettings"("databaseId"); + +-- CreateIndex +CREATE UNIQUE INDEX "PlausibleAnalytics_serviceId_key" ON "PlausibleAnalytics"("serviceId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Minio_serviceId_key" ON "Minio"("serviceId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Vscodeserver_serviceId_key" ON "Vscodeserver"("serviceId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId"); + +-- CreateIndex +CREATE UNIQUE INDEX "_TeamToUser_AB_unique" ON "_TeamToUser"("A", "B"); + +-- CreateIndex +CREATE INDEX "_TeamToUser_B_index" ON "_TeamToUser"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_ApplicationToTeam_AB_unique" ON "_ApplicationToTeam"("A", "B"); + +-- CreateIndex +CREATE INDEX "_ApplicationToTeam_B_index" ON "_ApplicationToTeam"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_GitSourceToTeam_AB_unique" ON "_GitSourceToTeam"("A", "B"); + +-- CreateIndex +CREATE INDEX "_GitSourceToTeam_B_index" ON "_GitSourceToTeam"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_GithubAppToTeam_AB_unique" ON "_GithubAppToTeam"("A", "B"); + +-- CreateIndex +CREATE INDEX "_GithubAppToTeam_B_index" ON "_GithubAppToTeam"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_GitlabAppToTeam_AB_unique" ON "_GitlabAppToTeam"("A", "B"); + +-- CreateIndex +CREATE INDEX "_GitlabAppToTeam_B_index" ON "_GitlabAppToTeam"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_DestinationDockerToTeam_AB_unique" ON "_DestinationDockerToTeam"("A", "B"); + +-- CreateIndex +CREATE INDEX "_DestinationDockerToTeam_B_index" ON "_DestinationDockerToTeam"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_DatabaseToTeam_AB_unique" ON "_DatabaseToTeam"("A", "B"); + +-- CreateIndex +CREATE INDEX "_DatabaseToTeam_B_index" ON "_DatabaseToTeam"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_ServiceToTeam_AB_unique" ON "_ServiceToTeam"("A", "B"); + +-- CreateIndex +CREATE INDEX "_ServiceToTeam_B_index" ON "_ServiceToTeam"("B"); diff --git a/apps/server/prisma/migrations/20220210104005_redis_aol/migration.sql b/apps/server/prisma/migrations/20220210104005_redis_aol/migration.sql new file mode 100644 index 000000000..8a7e41098 --- /dev/null +++ b/apps/server/prisma/migrations/20220210104005_redis_aol/migration.sql @@ -0,0 +1,28 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Team" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "databaseId" TEXT, + "serviceId" TEXT +); +INSERT INTO "new_Team" ("createdAt", "databaseId", "id", "name", "serviceId", "updatedAt") SELECT "createdAt", "databaseId", "id", "name", "serviceId", "updatedAt" FROM "Team"; +DROP TABLE "Team"; +ALTER TABLE "new_Team" RENAME TO "Team"; +CREATE TABLE "new_DatabaseSettings" ( + "id" TEXT NOT NULL PRIMARY KEY, + "databaseId" TEXT NOT NULL, + "isPublic" BOOLEAN NOT NULL DEFAULT false, + "appendOnly" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "DatabaseSettings_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_DatabaseSettings" ("createdAt", "databaseId", "id", "isPublic", "updatedAt") SELECT "createdAt", "databaseId", "id", "isPublic", "updatedAt" FROM "DatabaseSettings"; +DROP TABLE "DatabaseSettings"; +ALTER TABLE "new_DatabaseSettings" RENAME TO "DatabaseSettings"; +CREATE UNIQUE INDEX "DatabaseSettings_databaseId_key" ON "DatabaseSettings"("databaseId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220212142309_unique_secret_by_application/migration.sql b/apps/server/prisma/migrations/20220212142309_unique_secret_by_application/migration.sql new file mode 100644 index 000000000..da4aa8e7c --- /dev/null +++ b/apps/server/prisma/migrations/20220212142309_unique_secret_by_application/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - A unique constraint covering the columns `[name,applicationId]` on the table `Secret` will be added. If there are existing duplicate values, this will fail. + +*/ +-- DropIndex +DROP INDEX "Secret_name_key"; + +-- CreateIndex +CREATE UNIQUE INDEX "Secret_name_applicationId_key" ON "Secret"("name", "applicationId"); diff --git a/apps/server/prisma/migrations/20220217211304_dualcerts/migration.sql b/apps/server/prisma/migrations/20220217211304_dualcerts/migration.sql new file mode 100644 index 000000000..a6ea0a57d --- /dev/null +++ b/apps/server/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/apps/server/prisma/migrations/20220219231255_prmr_secrets/migration.sql b/apps/server/prisma/migrations/20220219231255_prmr_secrets/migration.sql new file mode 100644 index 000000000..728fcae36 --- /dev/null +++ b/apps/server/prisma/migrations/20220219231255_prmr_secrets/migration.sql @@ -0,0 +1,19 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Secret" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "value" TEXT NOT NULL, + "isPRMRSecret" BOOLEAN NOT NULL DEFAULT false, + "isBuildSecret" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "applicationId" TEXT NOT NULL, + CONSTRAINT "Secret_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Secret" ("applicationId", "createdAt", "id", "isBuildSecret", "name", "updatedAt", "value") SELECT "applicationId", "createdAt", "id", "isBuildSecret", "name", "updatedAt", "value" FROM "Secret"; +DROP TABLE "Secret"; +ALTER TABLE "new_Secret" RENAME TO "Secret"; +CREATE UNIQUE INDEX "Secret_name_applicationId_isPRMRSecret_key" ON "Secret"("name", "applicationId", "isPRMRSecret"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220220141136_public_portrange/migration.sql b/apps/server/prisma/migrations/20220220141136_public_portrange/migration.sql new file mode 100644 index 000000000..6423e9761 --- /dev/null +++ b/apps/server/prisma/migrations/20220220141136_public_portrange/migration.sql @@ -0,0 +1,20 @@ +-- 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, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "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"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220301101928_proxyhash/migration.sql b/apps/server/prisma/migrations/20220301101928_proxyhash/migration.sql new file mode 100644 index 000000000..87845b38b --- /dev/null +++ b/apps/server/prisma/migrations/20220301101928_proxyhash/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Setting" ADD COLUMN "proxyHash" TEXT; diff --git a/apps/server/prisma/migrations/20220304141408_service_secrets/migration.sql b/apps/server/prisma/migrations/20220304141408_service_secrets/migration.sql new file mode 100644 index 000000000..baa0c3f54 --- /dev/null +++ b/apps/server/prisma/migrations/20220304141408_service_secrets/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "ServiceSecret" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "value" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "serviceId" TEXT NOT NULL, + CONSTRAINT "ServiceSecret_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "ServiceSecret_name_serviceId_key" ON "ServiceSecret"("name", "serviceId"); diff --git a/apps/server/prisma/migrations/20220311213422_autodeploy/migration.sql b/apps/server/prisma/migrations/20220311213422_autodeploy/migration.sql new file mode 100644 index 000000000..d534d9372 --- /dev/null +++ b/apps/server/prisma/migrations/20220311213422_autodeploy/migration.sql @@ -0,0 +1,19 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +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, + "autodeploy" BOOLEAN NOT NULL DEFAULT true, + "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", "dualCerts", "id", "previews", "updatedAt") SELECT "applicationId", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt" FROM "ApplicationSettings"; +DROP TABLE "ApplicationSettings"; +ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; +CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220320141424_phpmodules/migration.sql b/apps/server/prisma/migrations/20220320141424_phpmodules/migration.sql new file mode 100644 index 000000000..6a17ff8a3 --- /dev/null +++ b/apps/server/prisma/migrations/20220320141424_phpmodules/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "phpModules" TEXT; diff --git a/apps/server/prisma/migrations/20220322135800_persistent_storage/migration.sql b/apps/server/prisma/migrations/20220322135800_persistent_storage/migration.sql new file mode 100644 index 000000000..d26ae3f8b --- /dev/null +++ b/apps/server/prisma/migrations/20220322135800_persistent_storage/migration.sql @@ -0,0 +1,18 @@ +-- CreateTable +CREATE TABLE "ApplicationPersistentStorage" ( + "id" TEXT NOT NULL PRIMARY KEY, + "applicationId" TEXT NOT NULL, + "path" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ApplicationPersistentStorage_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "ApplicationPersistentStorage_applicationId_key" ON "ApplicationPersistentStorage"("applicationId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ApplicationPersistentStorage_path_key" ON "ApplicationPersistentStorage"("path"); + +-- CreateIndex +CREATE UNIQUE INDEX "ApplicationPersistentStorage_applicationId_path_key" ON "ApplicationPersistentStorage"("applicationId", "path"); diff --git a/apps/server/prisma/migrations/20220327180323_ghost/migration.sql b/apps/server/prisma/migrations/20220327180323_ghost/migration.sql new file mode 100644 index 000000000..3c1cec36f --- /dev/null +++ b/apps/server/prisma/migrations/20220327180323_ghost/migration.sql @@ -0,0 +1,19 @@ +-- CreateTable +CREATE TABLE "Ghost" ( + "id" TEXT NOT NULL PRIMARY KEY, + "defaultEmail" TEXT NOT NULL, + "defaultPassword" TEXT NOT NULL, + "mariadbUser" TEXT NOT NULL, + "mariadbPassword" TEXT NOT NULL, + "mariadbRootUser" TEXT NOT NULL, + "mariadbRootUserPassword" TEXT NOT NULL, + "mariadbDatabase" TEXT, + "mariadbPublicPort" INTEGER, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Ghost_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Ghost_serviceId_key" ON "Ghost"("serviceId"); diff --git a/apps/server/prisma/migrations/20220402135305_python/migration.sql b/apps/server/prisma/migrations/20220402135305_python/migration.sql new file mode 100644 index 000000000..0c253d539 --- /dev/null +++ b/apps/server/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/apps/server/prisma/migrations/20220402210645_meilisearch/migration.sql b/apps/server/prisma/migrations/20220402210645_meilisearch/migration.sql new file mode 100644 index 000000000..9e832b107 --- /dev/null +++ b/apps/server/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/apps/server/prisma/migrations/20220405151428_wordpress_sftp/migration.sql b/apps/server/prisma/migrations/20220405151428_wordpress_sftp/migration.sql new file mode 100644 index 000000000..6c3c4b907 --- /dev/null +++ b/apps/server/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/apps/server/prisma/migrations/20220407220809_unique_storage_fix/migration.sql b/apps/server/prisma/migrations/20220407220809_unique_storage_fix/migration.sql new file mode 100644 index 000000000..9b645cc3b --- /dev/null +++ b/apps/server/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/apps/server/prisma/migrations/20220408070805_added_expose_port/migration.sql b/apps/server/prisma/migrations/20220408070805_added_expose_port/migration.sql new file mode 100644 index 000000000..a23afd64a --- /dev/null +++ b/apps/server/prisma/migrations/20220408070805_added_expose_port/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "exposePort" INTEGER; diff --git a/apps/server/prisma/migrations/20220418214843_persistent_storage_services/migration.sql b/apps/server/prisma/migrations/20220418214843_persistent_storage_services/migration.sql new file mode 100644 index 000000000..f85fd31df --- /dev/null +++ b/apps/server/prisma/migrations/20220418214843_persistent_storage_services/migration.sql @@ -0,0 +1,12 @@ +-- CreateTable +CREATE TABLE "ServicePersistentStorage" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "path" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ServicePersistentStorage_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_path_key" ON "ServicePersistentStorage"("serviceId", "path"); diff --git a/apps/server/prisma/migrations/20220419203408_multiply_dockerfile_locations/migration.sql b/apps/server/prisma/migrations/20220419203408_multiply_dockerfile_locations/migration.sql new file mode 100644 index 000000000..ce32f0844 --- /dev/null +++ b/apps/server/prisma/migrations/20220419203408_multiply_dockerfile_locations/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "dockerFileLocation" TEXT; diff --git a/apps/server/prisma/migrations/20220420202031_deno_configurations/migration.sql b/apps/server/prisma/migrations/20220420202031_deno_configurations/migration.sql new file mode 100644 index 000000000..9d0e832dc --- /dev/null +++ b/apps/server/prisma/migrations/20220420202031_deno_configurations/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "denoMainFile" TEXT; +ALTER TABLE "Application" ADD COLUMN "denoOptions" TEXT; diff --git a/apps/server/prisma/migrations/20220420210057_branch_for_builds/migration.sql b/apps/server/prisma/migrations/20220420210057_branch_for_builds/migration.sql new file mode 100644 index 000000000..8cd8e653d --- /dev/null +++ b/apps/server/prisma/migrations/20220420210057_branch_for_builds/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Build" ADD COLUMN "branch" TEXT; diff --git a/apps/server/prisma/migrations/20220425071132_umami/migration.sql b/apps/server/prisma/migrations/20220425071132_umami/migration.sql new file mode 100644 index 000000000..259fb76cd --- /dev/null +++ b/apps/server/prisma/migrations/20220425071132_umami/migration.sql @@ -0,0 +1,17 @@ +-- CreateTable +CREATE TABLE "Umami" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "umamiAdminPassword" TEXT NOT NULL, + "hashSalt" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Umami_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Umami_serviceId_key" ON "Umami"("serviceId"); diff --git a/apps/server/prisma/migrations/20220425075326_auto_update_coolify/migration.sql b/apps/server/prisma/migrations/20220425075326_auto_update_coolify/migration.sql new file mode 100644 index 000000000..a102973ee --- /dev/null +++ b/apps/server/prisma/migrations/20220425075326_auto_update_coolify/migration.sql @@ -0,0 +1,22 @@ +-- 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, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "proxyHash" TEXT, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220426125053_select_base_image/migration.sql b/apps/server/prisma/migrations/20220426125053_select_base_image/migration.sql new file mode 100644 index 000000000..37209d82f --- /dev/null +++ b/apps/server/prisma/migrations/20220426125053_select_base_image/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "baseBuildImage" TEXT; +ALTER TABLE "Application" ADD COLUMN "baseImage" TEXT; diff --git a/apps/server/prisma/migrations/20220427133656_hasura/migration.sql b/apps/server/prisma/migrations/20220427133656_hasura/migration.sql new file mode 100644 index 000000000..c679ad0fb --- /dev/null +++ b/apps/server/prisma/migrations/20220427133656_hasura/migration.sql @@ -0,0 +1,16 @@ +-- CreateTable +CREATE TABLE "Hasura" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "graphQLAdminPassword" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Hasura_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Hasura_serviceId_key" ON "Hasura"("serviceId"); diff --git a/apps/server/prisma/migrations/20220429202516_fider/migration.sql b/apps/server/prisma/migrations/20220429202516_fider/migration.sql new file mode 100644 index 000000000..a6d31a24d --- /dev/null +++ b/apps/server/prisma/migrations/20220429202516_fider/migration.sql @@ -0,0 +1,25 @@ +-- CreateTable +CREATE TABLE "Fider" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "jwtSecret" TEXT NOT NULL, + "emailNoreply" TEXT, + "emailMailgunApiKey" TEXT, + "emailMailgunDomain" TEXT, + "emailMailgunRegion" TEXT, + "emailSmtpHost" TEXT, + "emailSmtpPort" INTEGER, + "emailSmtpUser" TEXT, + "emailSmtpPassword" TEXT, + "emailSmtpEnableStartTls" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Fider_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Fider_serviceId_key" ON "Fider"("serviceId"); diff --git a/apps/server/prisma/migrations/20220429214112_fider_correction/migration.sql b/apps/server/prisma/migrations/20220429214112_fider_correction/migration.sql new file mode 100644 index 000000000..429524f72 --- /dev/null +++ b/apps/server/prisma/migrations/20220429214112_fider_correction/migration.sql @@ -0,0 +1,29 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Fider" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "jwtSecret" TEXT NOT NULL, + "emailNoreply" TEXT, + "emailMailgunApiKey" TEXT, + "emailMailgunDomain" TEXT, + "emailMailgunRegion" TEXT NOT NULL DEFAULT 'EU', + "emailSmtpHost" TEXT, + "emailSmtpPort" INTEGER, + "emailSmtpUser" TEXT, + "emailSmtpPassword" TEXT, + "emailSmtpEnableStartTls" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Fider_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Fider" ("createdAt", "emailMailgunApiKey", "emailMailgunDomain", "emailMailgunRegion", "emailNoreply", "emailSmtpEnableStartTls", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", "emailSmtpUser", "id", "jwtSecret", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "serviceId", "updatedAt") SELECT "createdAt", "emailMailgunApiKey", "emailMailgunDomain", coalesce("emailMailgunRegion", 'EU') AS "emailMailgunRegion", "emailNoreply", "emailSmtpEnableStartTls", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", "emailSmtpUser", "id", "jwtSecret", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "serviceId", "updatedAt" FROM "Fider"; +DROP TABLE "Fider"; +ALTER TABLE "new_Fider" RENAME TO "Fider"; +CREATE UNIQUE INDEX "Fider_serviceId_key" ON "Fider"("serviceId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql b/apps/server/prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql new file mode 100644 index 000000000..cf57379ca --- /dev/null +++ b/apps/server/prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql @@ -0,0 +1,23 @@ +-- 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, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "proxyHash" TEXT, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220430124553_expose_port_for_services/migration.sql b/apps/server/prisma/migrations/20220430124553_expose_port_for_services/migration.sql new file mode 100644 index 000000000..fdbab5713 --- /dev/null +++ b/apps/server/prisma/migrations/20220430124553_expose_port_for_services/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Service" ADD COLUMN "exposePort" INTEGER; diff --git a/apps/server/prisma/migrations/20220509130501_custom_plausible_script/migration.sql b/apps/server/prisma/migrations/20220509130501_custom_plausible_script/migration.sql new file mode 100644 index 000000000..6c8c28ff4 --- /dev/null +++ b/apps/server/prisma/migrations/20220509130501_custom_plausible_script/migration.sql @@ -0,0 +1,24 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_PlausibleAnalytics" ( + "id" TEXT NOT NULL PRIMARY KEY, + "email" TEXT, + "username" TEXT, + "password" TEXT NOT NULL, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "secretKeyBase" TEXT, + "scriptName" TEXT NOT NULL DEFAULT 'plausible.js', + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "PlausibleAnalytics_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_PlausibleAnalytics" ("createdAt", "email", "id", "password", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "serviceId", "updatedAt", "username") SELECT "createdAt", "email", "id", "password", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "serviceId", "updatedAt", "username" FROM "PlausibleAnalytics"; +DROP TABLE "PlausibleAnalytics"; +ALTER TABLE "new_PlausibleAnalytics" RENAME TO "PlausibleAnalytics"; +CREATE UNIQUE INDEX "PlausibleAnalytics_serviceId_key" ON "PlausibleAnalytics"("serviceId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220510081125_custom_wordpress_db/migration.sql b/apps/server/prisma/migrations/20220510081125_custom_wordpress_db/migration.sql new file mode 100644 index 000000000..5a1cd301c --- /dev/null +++ b/apps/server/prisma/migrations/20220510081125_custom_wordpress_db/migration.sql @@ -0,0 +1,32 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Wordpress" ( + "id" TEXT NOT NULL PRIMARY KEY, + "extraConfig" TEXT, + "tablePrefix" TEXT, + "ownMysql" BOOLEAN NOT NULL DEFAULT false, + "mysqlHost" TEXT, + "mysqlPort" INTEGER, + "mysqlUser" TEXT NOT NULL, + "mysqlPassword" TEXT NOT NULL, + "mysqlRootUser" TEXT NOT NULL, + "mysqlRootUserPassword" TEXT NOT NULL, + "mysqlDatabase" TEXT, + "mysqlPublicPort" INTEGER, + "ftpEnabled" BOOLEAN NOT NULL DEFAULT false, + "ftpUser" TEXT, + "ftpPassword" TEXT, + "ftpPublicPort" INTEGER, + "ftpHostKey" TEXT, + "ftpHostKeyPrivate" TEXT, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress"; +DROP TABLE "Wordpress"; +ALTER TABLE "new_Wordpress" RENAME TO "Wordpress"; +CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220517081328_traefik/migration.sql b/apps/server/prisma/migrations/20220517081328_traefik/migration.sql new file mode 100644 index 000000000..a83281fa4 --- /dev/null +++ b/apps/server/prisma/migrations/20220517081328_traefik/migration.sql @@ -0,0 +1,24 @@ +-- 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, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "proxyHash" TEXT, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220519095648_minio_apifqdn/migration.sql b/apps/server/prisma/migrations/20220519095648_minio_apifqdn/migration.sql new file mode 100644 index 000000000..a44712864 --- /dev/null +++ b/apps/server/prisma/migrations/20220519095648_minio_apifqdn/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Minio" ADD COLUMN "apiFqdn" TEXT; diff --git a/apps/server/prisma/migrations/20220708132655_deployment_type_for_applications/migration.sql b/apps/server/prisma/migrations/20220708132655_deployment_type_for_applications/migration.sql new file mode 100644 index 000000000..2776d1b88 --- /dev/null +++ b/apps/server/prisma/migrations/20220708132655_deployment_type_for_applications/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "deploymentType" TEXT; diff --git a/apps/server/prisma/migrations/20220712083523_custom_port_git_sources/migration.sql b/apps/server/prisma/migrations/20220712083523_custom_port_git_sources/migration.sql new file mode 100644 index 000000000..f702291b2 --- /dev/null +++ b/apps/server/prisma/migrations/20220712083523_custom_port_git_sources/migration.sql @@ -0,0 +1,24 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_GitSource" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "type" TEXT, + "apiUrl" TEXT, + "htmlUrl" TEXT, + "customPort" INTEGER NOT NULL DEFAULT 22, + "organization" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "githubAppId" TEXT, + "gitlabAppId" TEXT, + CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "githubAppId", "gitlabAppId", "htmlUrl", "id", "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; diff --git a/apps/server/prisma/migrations/20220718083646_moodle/migration.sql b/apps/server/prisma/migrations/20220718083646_moodle/migration.sql new file mode 100644 index 000000000..eaed14a4a --- /dev/null +++ b/apps/server/prisma/migrations/20220718083646_moodle/migration.sql @@ -0,0 +1,20 @@ +-- CreateTable +CREATE TABLE "Moodle" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "defaultUsername" TEXT NOT NULL, + "defaultPassword" TEXT NOT NULL, + "defaultEmail" TEXT NOT NULL, + "mariadbUser" TEXT NOT NULL, + "mariadbPassword" TEXT NOT NULL, + "mariadbRootUser" TEXT NOT NULL, + "mariadbRootUserPassword" TEXT NOT NULL, + "mariadbDatabase" TEXT NOT NULL, + "mariadbPublicPort" INTEGER, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Moodle_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Moodle_serviceId_key" ON "Moodle"("serviceId"); diff --git a/apps/server/prisma/migrations/20220718114551_remote_docker_engine/migration.sql b/apps/server/prisma/migrations/20220718114551_remote_docker_engine/migration.sql new file mode 100644 index 000000000..e80df22f2 --- /dev/null +++ b/apps/server/prisma/migrations/20220718114551_remote_docker_engine/migration.sql @@ -0,0 +1,21 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_DestinationDocker" ( + "id" TEXT NOT NULL PRIMARY KEY, + "network" TEXT NOT NULL, + "name" TEXT NOT NULL, + "engine" TEXT, + "remoteEngine" BOOLEAN NOT NULL DEFAULT false, + "remoteIpAddress" TEXT, + "remoteUser" TEXT, + "remotePort" INTEGER, + "isCoolifyProxyUsed" BOOLEAN DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_DestinationDocker" ("createdAt", "engine", "id", "isCoolifyProxyUsed", "name", "network", "remoteEngine", "updatedAt") SELECT "createdAt", "engine", "id", "isCoolifyProxyUsed", "name", "network", "remoteEngine", "updatedAt" FROM "DestinationDocker"; +DROP TABLE "DestinationDocker"; +ALTER TABLE "new_DestinationDocker" RENAME TO "DestinationDocker"; +CREATE UNIQUE INDEX "DestinationDocker_network_key" ON "DestinationDocker"("network"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220721084020_ssh_key/migration.sql b/apps/server/prisma/migrations/20220721084020_ssh_key/migration.sql new file mode 100644 index 000000000..8323af409 --- /dev/null +++ b/apps/server/prisma/migrations/20220721084020_ssh_key/migration.sql @@ -0,0 +1,33 @@ +-- CreateTable +CREATE TABLE "SshKey" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "privateKey" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_DestinationDocker" ( + "id" TEXT NOT NULL PRIMARY KEY, + "network" TEXT NOT NULL, + "name" TEXT NOT NULL, + "engine" TEXT, + "remoteEngine" BOOLEAN NOT NULL DEFAULT false, + "remoteIpAddress" TEXT, + "remoteUser" TEXT, + "remotePort" INTEGER, + "remoteVerified" BOOLEAN NOT NULL DEFAULT false, + "isCoolifyProxyUsed" BOOLEAN DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "sshKeyId" TEXT, + CONSTRAINT "DestinationDocker_sshKeyId_fkey" FOREIGN KEY ("sshKeyId") REFERENCES "SshKey" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_DestinationDocker" ("createdAt", "engine", "id", "isCoolifyProxyUsed", "name", "network", "remoteEngine", "remoteIpAddress", "remotePort", "remoteUser", "updatedAt") SELECT "createdAt", "engine", "id", "isCoolifyProxyUsed", "name", "network", "remoteEngine", "remoteIpAddress", "remotePort", "remoteUser", "updatedAt" FROM "DestinationDocker"; +DROP TABLE "DestinationDocker"; +ALTER TABLE "new_DestinationDocker" RENAME TO "DestinationDocker"; +CREATE UNIQUE INDEX "DestinationDocker_network_key" ON "DestinationDocker"("network"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220722203927_ipaddress/migration.sql b/apps/server/prisma/migrations/20220722203927_ipaddress/migration.sql new file mode 100644 index 000000000..21d45ada3 --- /dev/null +++ b/apps/server/prisma/migrations/20220722203927_ipaddress/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Setting" ADD COLUMN "ipv4" TEXT; +ALTER TABLE "Setting" ADD COLUMN "ipv6" TEXT; diff --git a/apps/server/prisma/migrations/20220725191205_architecture/migration.sql b/apps/server/prisma/migrations/20220725191205_architecture/migration.sql new file mode 100644 index 000000000..2e0ff3e01 --- /dev/null +++ b/apps/server/prisma/migrations/20220725191205_architecture/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Setting" ADD COLUMN "arch" TEXT; diff --git a/apps/server/prisma/migrations/20220726121333_fix_ssh_key/migration.sql b/apps/server/prisma/migrations/20220726121333_fix_ssh_key/migration.sql new file mode 100644 index 000000000..e6e47b197 --- /dev/null +++ b/apps/server/prisma/migrations/20220726121333_fix_ssh_key/migration.sql @@ -0,0 +1,16 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_SshKey" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "privateKey" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "teamId" TEXT, + CONSTRAINT "SshKey_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_SshKey" ("createdAt", "id", "name", "privateKey", "updatedAt") SELECT "createdAt", "id", "name", "privateKey", "updatedAt" FROM "SshKey"; +DROP TABLE "SshKey"; +ALTER TABLE "new_SshKey" RENAME TO "SshKey"; +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220806090621_fqdn_not_unique_anymore/migration.sql b/apps/server/prisma/migrations/20220806090621_fqdn_not_unique_anymore/migration.sql new file mode 100644 index 000000000..f1eab666b --- /dev/null +++ b/apps/server/prisma/migrations/20220806090621_fqdn_not_unique_anymore/migration.sql @@ -0,0 +1,2 @@ +-- DropIndex +DROP INDEX "Application_fqdn_key"; diff --git a/apps/server/prisma/migrations/20220806102340_rde_ssh_local_port/migration.sql b/apps/server/prisma/migrations/20220806102340_rde_ssh_local_port/migration.sql new file mode 100644 index 000000000..ae4c39846 --- /dev/null +++ b/apps/server/prisma/migrations/20220806102340_rde_ssh_local_port/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "DestinationDocker" ADD COLUMN "sshLocalPort" INTEGER; diff --git a/apps/server/prisma/migrations/20220815092230_glitchtip/migration.sql b/apps/server/prisma/migrations/20220815092230_glitchtip/migration.sql new file mode 100644 index 000000000..dba98ab82 --- /dev/null +++ b/apps/server/prisma/migrations/20220815092230_glitchtip/migration.sql @@ -0,0 +1,30 @@ +-- CreateTable +CREATE TABLE "GlitchTip" ( + "id" TEXT NOT NULL PRIMARY KEY, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "secretKeyBase" TEXT, + "defaultEmail" TEXT NOT NULL, + "defaultUsername" TEXT NOT NULL, + "defaultPassword" TEXT NOT NULL, + "defaultEmailFrom" TEXT NOT NULL DEFAULT 'glitchtip@domain.tdl', + "emailSmtpHost" TEXT DEFAULT 'domain.tdl', + "emailSmtpPort" INTEGER DEFAULT 25, + "emailSmtpUser" TEXT, + "emailSmtpPassword" TEXT, + "emailSmtpUseTls" BOOLEAN DEFAULT false, + "emailSmtpUseSsl" BOOLEAN DEFAULT false, + "emailBackend" TEXT, + "mailgunApiKey" TEXT, + "sendgridApiKey" TEXT, + "enableOpenUserRegistration" BOOLEAN NOT NULL DEFAULT true, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "GlitchTip_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "GlitchTip_serviceId_key" ON "GlitchTip"("serviceId"); diff --git a/apps/server/prisma/migrations/20220815133844_appwrite/migration.sql b/apps/server/prisma/migrations/20220815133844_appwrite/migration.sql new file mode 100644 index 000000000..ec61dc434 --- /dev/null +++ b/apps/server/prisma/migrations/20220815133844_appwrite/migration.sql @@ -0,0 +1,22 @@ +-- CreateTable +CREATE TABLE "Appwrite" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "opensslKeyV1" TEXT NOT NULL, + "executorSecret" TEXT NOT NULL, + "redisPassword" TEXT NOT NULL, + "mariadbHost" TEXT, + "mariadbPort" INTEGER NOT NULL DEFAULT 3306, + "mariadbUser" TEXT NOT NULL, + "mariadbPassword" TEXT NOT NULL, + "mariadbRootUser" TEXT NOT NULL, + "mariadbRootUserPassword" TEXT NOT NULL, + "mariadbDatabase" TEXT NOT NULL, + "mariadbPublicPort" INTEGER, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Appwrite_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Appwrite_serviceId_key" ON "Appwrite"("serviceId"); diff --git a/apps/server/prisma/migrations/20220816133447_bot_deployments/migration.sql b/apps/server/prisma/migrations/20220816133447_bot_deployments/migration.sql new file mode 100644 index 000000000..3d6d92d0e --- /dev/null +++ b/apps/server/prisma/migrations/20220816133447_bot_deployments/migration.sql @@ -0,0 +1,20 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +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, + "autodeploy" BOOLEAN NOT NULL DEFAULT true, + "isBot" 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", "autodeploy", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt" FROM "ApplicationSettings"; +DROP TABLE "ApplicationSettings"; +ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; +CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220817082342_custom_dns_servers/migration.sql b/apps/server/prisma/migrations/20220817082342_custom_dns_servers/migration.sql new file mode 100644 index 000000000..03588b549 --- /dev/null +++ b/apps/server/prisma/migrations/20220817082342_custom_dns_servers/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Setting" ADD COLUMN "DNSServers" TEXT; diff --git a/apps/server/prisma/migrations/20220818093615_public_repositories/migration.sql b/apps/server/prisma/migrations/20220818093615_public_repositories/migration.sql new file mode 100644 index 000000000..7f08c29fd --- /dev/null +++ b/apps/server/prisma/migrations/20220818093615_public_repositories/migration.sql @@ -0,0 +1,42 @@ +-- 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, + "organization" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "githubAppId" TEXT, + "gitlabAppId" TEXT, + CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "githubAppId", "gitlabAppId", "htmlUrl", "id", "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"); +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, + "autodeploy" BOOLEAN NOT NULL DEFAULT true, + "isBot" BOOLEAN NOT NULL DEFAULT false, + "isPublicRepository" 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", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "previews", "updatedAt" FROM "ApplicationSettings"; +DROP TABLE "ApplicationSettings"; +ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; +CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220823070532_service_searxng/migration.sql b/apps/server/prisma/migrations/20220823070532_service_searxng/migration.sql new file mode 100644 index 000000000..81ecc81c8 --- /dev/null +++ b/apps/server/prisma/migrations/20220823070532_service_searxng/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "Searxng" ( + "id" TEXT NOT NULL PRIMARY KEY, + "secretKey" TEXT NOT NULL, + "redisPassword" TEXT NOT NULL, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Searxng_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Searxng_serviceId_key" ON "Searxng"("serviceId"); diff --git a/apps/server/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql b/apps/server/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql new file mode 100644 index 000000000..1535c2bf7 --- /dev/null +++ b/apps/server/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql @@ -0,0 +1,29 @@ +-- 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, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "proxyHash" TEXT, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "DNSServers" TEXT, + "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "ipv4" TEXT, + "ipv6" TEXT, + "arch" TEXT, + "concurrentBuilds" INTEGER NOT NULL DEFAULT 1 +); +INSERT INTO "new_Setting" ("DNSServers", "arch", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "DNSServers", "arch", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220825072007_build_queue_improvements/migration.sql b/apps/server/prisma/migrations/20220825072007_build_queue_improvements/migration.sql new file mode 100644 index 000000000..78c51fc15 --- /dev/null +++ b/apps/server/prisma/migrations/20220825072007_build_queue_improvements/migration.sql @@ -0,0 +1,24 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Build" ( + "id" TEXT NOT NULL PRIMARY KEY, + "type" TEXT NOT NULL, + "applicationId" TEXT, + "destinationDockerId" TEXT, + "gitSourceId" TEXT, + "githubAppId" TEXT, + "gitlabAppId" TEXT, + "commit" TEXT, + "pullmergeRequestId" TEXT, + "forceRebuild" BOOLEAN NOT NULL DEFAULT false, + "sourceBranch" TEXT, + "branch" TEXT, + "status" TEXT DEFAULT 'queued', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Build" ("applicationId", "branch", "commit", "createdAt", "destinationDockerId", "gitSourceId", "githubAppId", "gitlabAppId", "id", "status", "type", "updatedAt") SELECT "applicationId", "branch", "commit", "createdAt", "destinationDockerId", "gitSourceId", "githubAppId", "gitlabAppId", "id", "status", "type", "updatedAt" FROM "Build"; +DROP TABLE "Build"; +ALTER TABLE "new_Build" RENAME TO "Build"; +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220831095714_service_weblate/migration.sql b/apps/server/prisma/migrations/20220831095714_service_weblate/migration.sql new file mode 100644 index 000000000..c985b4ae2 --- /dev/null +++ b/apps/server/prisma/migrations/20220831095714_service_weblate/migration.sql @@ -0,0 +1,18 @@ +-- CreateTable +CREATE TABLE "Weblate" ( + "id" TEXT NOT NULL PRIMARY KEY, + "adminPassword" TEXT NOT NULL, + "postgresqlHost" TEXT NOT NULL, + "postgresqlPort" INTEGER NOT NULL, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Weblate_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Weblate_serviceId_key" ON "Weblate"("serviceId"); diff --git a/apps/server/prisma/migrations/20220902115640_service_taiga/migration.sql b/apps/server/prisma/migrations/20220902115640_service_taiga/migration.sql new file mode 100644 index 000000000..3035dd8ef --- /dev/null +++ b/apps/server/prisma/migrations/20220902115640_service_taiga/migration.sql @@ -0,0 +1,23 @@ +-- CreateTable +CREATE TABLE "Taiga" ( + "id" TEXT NOT NULL PRIMARY KEY, + "secretKey" TEXT NOT NULL, + "erlangSecret" TEXT NOT NULL, + "djangoAdminPassword" TEXT NOT NULL, + "djangoAdminUser" TEXT NOT NULL, + "rabbitMQUser" TEXT NOT NULL, + "rabbitMQPassword" TEXT NOT NULL, + "postgresqlHost" TEXT NOT NULL, + "postgresqlPort" INTEGER NOT NULL, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Taiga_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Taiga_serviceId_key" ON "Taiga"("serviceId"); diff --git a/apps/server/prisma/migrations/20220905062318_database_branching/migration.sql b/apps/server/prisma/migrations/20220905062318_database_branching/migration.sql new file mode 100644 index 000000000..d828a4c66 --- /dev/null +++ b/apps/server/prisma/migrations/20220905062318_database_branching/migration.sql @@ -0,0 +1,22 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +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, + "autodeploy" BOOLEAN NOT NULL DEFAULT true, + "isBot" BOOLEAN NOT NULL DEFAULT false, + "isPublicRepository" BOOLEAN NOT NULL DEFAULT false, + "isDBBranching" 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", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings"; +DROP TABLE "ApplicationSettings"; +ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; +CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220905113241_prisma_migration/migration.sql b/apps/server/prisma/migrations/20220905113241_prisma_migration/migration.sql new file mode 100644 index 000000000..324bcfff0 --- /dev/null +++ b/apps/server/prisma/migrations/20220905113241_prisma_migration/migration.sql @@ -0,0 +1,20 @@ +/* + Warnings: + + - You are about to alter the column `time` on the `BuildLog` table. The data in that column could be lost. The data in that column will be cast from `Int` to `BigInt`. + +*/ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_BuildLog" ( + "id" TEXT NOT NULL PRIMARY KEY, + "applicationId" TEXT, + "buildId" TEXT NOT NULL, + "line" TEXT NOT NULL, + "time" BIGINT NOT NULL +); +INSERT INTO "new_BuildLog" ("applicationId", "buildId", "id", "line", "time") SELECT "applicationId", "buildId", "id", "line", "time" FROM "BuildLog"; +DROP TABLE "BuildLog"; +ALTER TABLE "new_BuildLog" RENAME TO "BuildLog"; +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220905115321_application_connected_database/migration.sql b/apps/server/prisma/migrations/20220905115321_application_connected_database/migration.sql new file mode 100644 index 000000000..576c23bdf --- /dev/null +++ b/apps/server/prisma/migrations/20220905115321_application_connected_database/migration.sql @@ -0,0 +1,20 @@ +-- CreateTable +CREATE TABLE "ApplicationConnectedDatabase" ( + "id" TEXT NOT NULL PRIMARY KEY, + "applicationId" TEXT NOT NULL, + "databaseId" TEXT, + "hostedDatabaseType" TEXT, + "hostedDatabaseHost" TEXT, + "hostedDatabasePort" INTEGER, + "hostedDatabaseName" TEXT, + "hostedDatabaseUser" TEXT, + "hostedDatabasePassword" TEXT, + "hostedDatabaseDBName" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ApplicationConnectedDatabase_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "ApplicationConnectedDatabase_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "ApplicationConnectedDatabase_applicationId_key" ON "ApplicationConnectedDatabase"("applicationId"); diff --git a/apps/server/prisma/migrations/20220906120112_enable_api_debug_logging/migration.sql b/apps/server/prisma/migrations/20220906120112_enable_api_debug_logging/migration.sql new file mode 100644 index 000000000..05fb3e285 --- /dev/null +++ b/apps/server/prisma/migrations/20220906120112_enable_api_debug_logging/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Setting" ADD COLUMN "isAPIDebuggingEnabled" BOOLEAN DEFAULT false; diff --git a/apps/server/prisma/migrations/20220907092244_database_secrets/migration.sql b/apps/server/prisma/migrations/20220907092244_database_secrets/migration.sql new file mode 100644 index 000000000..53ff2d19e --- /dev/null +++ b/apps/server/prisma/migrations/20220907092244_database_secrets/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "DatabaseSecret" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "value" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "databaseId" TEXT NOT NULL, + CONSTRAINT "DatabaseSecret_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "DatabaseSecret_name_databaseId_key" ON "DatabaseSecret"("name", "databaseId"); diff --git a/apps/server/prisma/migrations/20220913092100_preview_applications/migration.sql b/apps/server/prisma/migrations/20220913092100_preview_applications/migration.sql new file mode 100644 index 000000000..0ec1aafa0 --- /dev/null +++ b/apps/server/prisma/migrations/20220913092100_preview_applications/migration.sql @@ -0,0 +1,18 @@ +-- AlterTable +ALTER TABLE "Build" ADD COLUMN "previewApplicationId" TEXT; + +-- CreateTable +CREATE TABLE "PreviewApplication" ( + "id" TEXT NOT NULL PRIMARY KEY, + "pullmergeRequestId" TEXT NOT NULL, + "sourceBranch" TEXT NOT NULL, + "isRandomDomain" BOOLEAN NOT NULL DEFAULT false, + "customDomain" TEXT, + "applicationId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "PreviewApplication_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "PreviewApplication_applicationId_key" ON "PreviewApplication"("applicationId"); diff --git a/apps/server/prisma/migrations/20220922064605_custom_certificates/migration.sql b/apps/server/prisma/migrations/20220922064605_custom_certificates/migration.sql new file mode 100644 index 000000000..804913038 --- /dev/null +++ b/apps/server/prisma/migrations/20220922064605_custom_certificates/migration.sql @@ -0,0 +1,10 @@ +-- CreateTable +CREATE TABLE "Certificate" ( + "id" TEXT NOT NULL PRIMARY KEY, + "key" TEXT NOT NULL, + "cert" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "teamId" TEXT, + CONSTRAINT "Certificate_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); diff --git a/apps/server/prisma/migrations/20220923122227_custom_ssl_for_applications/migration.sql b/apps/server/prisma/migrations/20220923122227_custom_ssl_for_applications/migration.sql new file mode 100644 index 000000000..b9261a955 --- /dev/null +++ b/apps/server/prisma/migrations/20220923122227_custom_ssl_for_applications/migration.sql @@ -0,0 +1,23 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +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, + "autodeploy" BOOLEAN NOT NULL DEFAULT true, + "isBot" BOOLEAN NOT NULL DEFAULT false, + "isPublicRepository" BOOLEAN NOT NULL DEFAULT false, + "isDBBranching" BOOLEAN NOT NULL DEFAULT false, + "isCustomSSL" 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", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isDBBranching", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isDBBranching", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings"; +DROP TABLE "ApplicationSettings"; +ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; +CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20220928083348_system_wide_git_sources/migration.sql b/apps/server/prisma/migrations/20220928083348_system_wide_git_sources/migration.sql new file mode 100644 index 000000000..a3657504d --- /dev/null +++ b/apps/server/prisma/migrations/20220928083348_system_wide_git_sources/migration.sql @@ -0,0 +1,26 @@ +-- 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, + "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", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "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; diff --git a/apps/server/prisma/migrations/20221002084246_no_unique_appid_on_previews/migration.sql b/apps/server/prisma/migrations/20221002084246_no_unique_appid_on_previews/migration.sql new file mode 100644 index 000000000..031f842cb --- /dev/null +++ b/apps/server/prisma/migrations/20221002084246_no_unique_appid_on_previews/migration.sql @@ -0,0 +1,2 @@ +-- DropIndex +DROP INDEX "PreviewApplication_applicationId_key"; diff --git a/apps/server/prisma/migrations/20221002091630_forked_previews/migration.sql b/apps/server/prisma/migrations/20221002091630_forked_previews/migration.sql new file mode 100644 index 000000000..2312c011b --- /dev/null +++ b/apps/server/prisma/migrations/20221002091630_forked_previews/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Build" ADD COLUMN "sourceRepository" TEXT; diff --git a/apps/server/prisma/migrations/20221005120323_initial_docker_compose/migration.sql b/apps/server/prisma/migrations/20221005120323_initial_docker_compose/migration.sql new file mode 100644 index 000000000..bb93e1aaf --- /dev/null +++ b/apps/server/prisma/migrations/20221005120323_initial_docker_compose/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "dockerComposeFile" TEXT; +ALTER TABLE "Application" ADD COLUMN "dockerComposeFileLocation" TEXT; diff --git a/apps/server/prisma/migrations/20221005132352_docker_compose_configuration/migration.sql b/apps/server/prisma/migrations/20221005132352_docker_compose_configuration/migration.sql new file mode 100644 index 000000000..e7368dc1a --- /dev/null +++ b/apps/server/prisma/migrations/20221005132352_docker_compose_configuration/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "dockerComposeConfiguration" TEXT; diff --git a/apps/server/prisma/migrations/20221017134342_standardized_service_configs/migration.sql b/apps/server/prisma/migrations/20221017134342_standardized_service_configs/migration.sql new file mode 100644 index 000000000..837017435 --- /dev/null +++ b/apps/server/prisma/migrations/20221017134342_standardized_service_configs/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "ServiceSetting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "value" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ServiceSetting_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "ServiceSetting_serviceId_name_key" ON "ServiceSetting"("serviceId", "name"); diff --git a/apps/server/prisma/migrations/20221018090939_service_peristent_volumes_predefined/migration.sql b/apps/server/prisma/migrations/20221018090939_service_peristent_volumes_predefined/migration.sql new file mode 100644 index 000000000..c7b935eb7 --- /dev/null +++ b/apps/server/prisma/migrations/20221018090939_service_peristent_volumes_predefined/migration.sql @@ -0,0 +1,19 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_ServicePersistentStorage" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "path" TEXT NOT NULL, + "volumeName" TEXT, + "predefined" BOOLEAN NOT NULL DEFAULT false, + "containerId" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ServicePersistentStorage_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_ServicePersistentStorage" ("createdAt", "id", "path", "serviceId", "updatedAt") SELECT "createdAt", "id", "path", "serviceId", "updatedAt" FROM "ServicePersistentStorage"; +DROP TABLE "ServicePersistentStorage"; +ALTER TABLE "new_ServicePersistentStorage" RENAME TO "ServicePersistentStorage"; +CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_path_key" ON "ServicePersistentStorage"("serviceId", "path"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221021112429_serivce_settings_extended/migration.sql b/apps/server/prisma/migrations/20221021112429_serivce_settings_extended/migration.sql new file mode 100644 index 000000000..b2eddcfd9 --- /dev/null +++ b/apps/server/prisma/migrations/20221021112429_serivce_settings_extended/migration.sql @@ -0,0 +1,24 @@ +/* + Warnings: + + - Added the required column `variableName` to the `ServiceSetting` table without a default value. This is not possible if the table is not empty. + +*/ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_ServiceSetting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "serviceId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "value" TEXT NOT NULL, + "variableName" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ServiceSetting_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_ServiceSetting" ("createdAt", "id", "name", "serviceId", "updatedAt", "value") SELECT "createdAt", "id", "name", "serviceId", "updatedAt", "value" FROM "ServiceSetting"; +DROP TABLE "ServiceSetting"; +ALTER TABLE "new_ServiceSetting" RENAME TO "ServiceSetting"; +CREATE UNIQUE INDEX "ServiceSetting_serviceId_name_key" ON "ServiceSetting"("serviceId", "name"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221021185630_service_template_version/migration.sql b/apps/server/prisma/migrations/20221021185630_service_template_version/migration.sql new file mode 100644 index 000000000..f716a2340 --- /dev/null +++ b/apps/server/prisma/migrations/20221021185630_service_template_version/migration.sql @@ -0,0 +1,21 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Service" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "fqdn" TEXT, + "exposePort" INTEGER, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "type" TEXT, + "version" TEXT, + "templateVersion" TEXT NOT NULL DEFAULT '0.0.0', + "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", "dualCerts", "exposePort", "fqdn", "id", "name", "type", "updatedAt", "version") SELECT "createdAt", "destinationDockerId", "dualCerts", "exposePort", "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/apps/server/prisma/migrations/20221026115123_service_persistent_unique/migration.sql b/apps/server/prisma/migrations/20221026115123_service_persistent_unique/migration.sql new file mode 100644 index 000000000..dae80a76a --- /dev/null +++ b/apps/server/prisma/migrations/20221026115123_service_persistent_unique/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - A unique constraint covering the columns `[serviceId,containerId,path]` on the table `ServicePersistentStorage` will be added. If there are existing duplicate values, this will fail. + +*/ +-- DropIndex +DROP INDEX "ServicePersistentStorage_serviceId_path_key"; + +-- CreateIndex +CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_containerId_path_key" ON "ServicePersistentStorage"("serviceId", "containerId", "path"); diff --git a/apps/server/prisma/migrations/20221028074301_wordpress_optional_fields/migration.sql b/apps/server/prisma/migrations/20221028074301_wordpress_optional_fields/migration.sql new file mode 100644 index 000000000..217039d97 --- /dev/null +++ b/apps/server/prisma/migrations/20221028074301_wordpress_optional_fields/migration.sql @@ -0,0 +1,32 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Wordpress" ( + "id" TEXT NOT NULL PRIMARY KEY, + "extraConfig" TEXT, + "tablePrefix" TEXT, + "ownMysql" BOOLEAN NOT NULL DEFAULT false, + "mysqlHost" TEXT, + "mysqlPort" INTEGER, + "mysqlUser" TEXT, + "mysqlPassword" TEXT, + "mysqlRootUser" TEXT, + "mysqlRootUserPassword" TEXT, + "mysqlDatabase" TEXT, + "mysqlPublicPort" INTEGER, + "ftpEnabled" BOOLEAN NOT NULL DEFAULT false, + "ftpUser" TEXT, + "ftpPassword" TEXT, + "ftpPublicPort" INTEGER, + "ftpHostKey" TEXT, + "ftpHostKeyPrivate" TEXT, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlHost", "mysqlPassword", "mysqlPort", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "ownMysql", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlHost", "mysqlPassword", "mysqlPort", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "ownMysql", "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/apps/server/prisma/migrations/20221104092223_default_redirect_proxy/migration.sql b/apps/server/prisma/migrations/20221104092223_default_redirect_proxy/migration.sql new file mode 100644 index 000000000..8584fe25c --- /dev/null +++ b/apps/server/prisma/migrations/20221104092223_default_redirect_proxy/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Setting" ADD COLUMN "proxyDefaultRedirect" TEXT; diff --git a/apps/server/prisma/migrations/20221114093217_application_storage_path_migration/migration.sql b/apps/server/prisma/migrations/20221114093217_application_storage_path_migration/migration.sql new file mode 100644 index 000000000..6913c8b00 --- /dev/null +++ b/apps/server/prisma/migrations/20221114093217_application_storage_path_migration/migration.sql @@ -0,0 +1,45 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "isAPIDebuggingEnabled" BOOLEAN DEFAULT false, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "proxyHash" TEXT, + "proxyDefaultRedirect" TEXT, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "DNSServers" TEXT, + "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "ipv4" TEXT, + "ipv6" TEXT, + "arch" TEXT, + "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, + "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false +); +INSERT INTO "new_Setting" ("DNSServers", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "DNSServers", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "proxyHash", "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_ApplicationPersistentStorage" ( + "id" TEXT NOT NULL PRIMARY KEY, + "applicationId" TEXT NOT NULL, + "path" TEXT NOT NULL, + "oldPath" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ApplicationPersistentStorage_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_ApplicationPersistentStorage" ("applicationId", "createdAt", "id", "path", "updatedAt") SELECT "applicationId", "createdAt", "id", "path", "updatedAt" FROM "ApplicationPersistentStorage"; +DROP TABLE "ApplicationPersistentStorage"; +ALTER TABLE "new_ApplicationPersistentStorage" RENAME TO "ApplicationPersistentStorage"; +CREATE UNIQUE INDEX "ApplicationPersistentStorage_applicationId_path_key" ON "ApplicationPersistentStorage"("applicationId", "path"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221123122143_remote_haproxy_from_db/migration.sql b/apps/server/prisma/migrations/20221123122143_remote_haproxy_from_db/migration.sql new file mode 100644 index 000000000..1acf261a5 --- /dev/null +++ b/apps/server/prisma/migrations/20221123122143_remote_haproxy_from_db/migration.sql @@ -0,0 +1,37 @@ +/* + Warnings: + + - You are about to drop the column `proxyHash` on the `Setting` table. All the data in the column will be lost. + - You are about to drop the column `proxyPassword` on the `Setting` table. All the data in the column will be lost. + - You are about to drop the column `proxyUser` on the `Setting` table. All the data in the column will be lost. + +*/ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "DNSServers" TEXT, + "ipv4" TEXT, + "ipv6" TEXT, + "arch" TEXT, + "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, + "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, + "proxyDefaultRedirect" TEXT, + "isAPIDebuggingEnabled" BOOLEAN DEFAULT false, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221123133429_docker_registries/migration.sql b/apps/server/prisma/migrations/20221123133429_docker_registries/migration.sql new file mode 100644 index 000000000..abebc5514 --- /dev/null +++ b/apps/server/prisma/migrations/20221123133429_docker_registries/migration.sql @@ -0,0 +1,59 @@ +-- CreateTable +CREATE TABLE "DockerRegistry" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "url" TEXT NOT NULL, + "username" TEXT, + "password" TEXT, + "isSystemWide" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "teamId" TEXT, + CONSTRAINT "DockerRegistry_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Application" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "fqdn" TEXT, + "repository" TEXT, + "configHash" TEXT, + "branch" TEXT, + "buildPack" TEXT, + "projectId" INTEGER, + "port" INTEGER, + "exposePort" INTEGER, + "installCommand" TEXT, + "buildCommand" TEXT, + "startCommand" TEXT, + "baseDirectory" TEXT, + "publishDirectory" TEXT, + "deploymentType" TEXT, + "phpModules" TEXT, + "pythonWSGI" TEXT, + "pythonModule" TEXT, + "pythonVariable" TEXT, + "dockerFileLocation" TEXT, + "denoMainFile" TEXT, + "denoOptions" TEXT, + "dockerComposeFile" TEXT, + "dockerComposeFileLocation" TEXT, + "dockerComposeConfiguration" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "destinationDockerId" TEXT, + "gitSourceId" TEXT, + "baseImage" TEXT, + "baseBuildImage" TEXT, + "dockerRegistryId" TEXT NOT NULL DEFAULT '0', + CONSTRAINT "Application_gitSourceId_fkey" FOREIGN KEY ("gitSourceId") REFERENCES "GitSource" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "Application_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "Application_dockerRegistryId_fkey" FOREIGN KEY ("dockerRegistryId") REFERENCES "DockerRegistry" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Application" ("baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "exposePort", "fqdn", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt") SELECT "baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "exposePort", "fqdn", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt" FROM "Application"; +DROP TABLE "Application"; +ALTER TABLE "new_Application" RENAME TO "Application"; +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221128104158_do_not_track/migration.sql b/apps/server/prisma/migrations/20221128104158_do_not_track/migration.sql new file mode 100644 index 000000000..9cf26d8a8 --- /dev/null +++ b/apps/server/prisma/migrations/20221128104158_do_not_track/migration.sql @@ -0,0 +1,30 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "DNSServers" TEXT, + "ipv4" TEXT, + "ipv6" TEXT, + "arch" TEXT, + "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, + "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, + "proxyDefaultRedirect" TEXT, + "doNotTrack" BOOLEAN NOT NULL DEFAULT false, + "isAPIDebuggingEnabled" BOOLEAN DEFAULT false, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221128104718_fix_defaults/migration.sql b/apps/server/prisma/migrations/20221128104718_fix_defaults/migration.sql new file mode 100644 index 000000000..c1152cdc3 --- /dev/null +++ b/apps/server/prisma/migrations/20221128104718_fix_defaults/migration.sql @@ -0,0 +1,60 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "DNSServers" TEXT, + "ipv4" TEXT, + "ipv6" TEXT, + "arch" TEXT, + "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, + "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, + "proxyDefaultRedirect" TEXT, + "doNotTrack" BOOLEAN NOT NULL DEFAULT false, + "isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, + "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", "proxyDefaultRedirect", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", coalesce("isAPIDebuggingEnabled", false) AS "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "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_GlitchTip" ( + "id" TEXT NOT NULL PRIMARY KEY, + "postgresqlUser" TEXT NOT NULL, + "postgresqlPassword" TEXT NOT NULL, + "postgresqlDatabase" TEXT NOT NULL, + "postgresqlPublicPort" INTEGER, + "secretKeyBase" TEXT, + "defaultEmail" TEXT NOT NULL, + "defaultUsername" TEXT NOT NULL, + "defaultPassword" TEXT NOT NULL, + "defaultEmailFrom" TEXT NOT NULL DEFAULT 'glitchtip@domain.tdl', + "emailSmtpHost" TEXT DEFAULT 'domain.tdl', + "emailSmtpPort" INTEGER DEFAULT 25, + "emailSmtpUser" TEXT, + "emailSmtpPassword" TEXT, + "emailSmtpUseTls" BOOLEAN NOT NULL DEFAULT false, + "emailSmtpUseSsl" BOOLEAN NOT NULL DEFAULT false, + "emailBackend" TEXT, + "mailgunApiKey" TEXT, + "sendgridApiKey" TEXT, + "enableOpenUserRegistration" BOOLEAN NOT NULL DEFAULT true, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "GlitchTip_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_GlitchTip" ("createdAt", "defaultEmail", "defaultEmailFrom", "defaultPassword", "defaultUsername", "emailBackend", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", "emailSmtpUseSsl", "emailSmtpUseTls", "emailSmtpUser", "enableOpenUserRegistration", "id", "mailgunApiKey", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "sendgridApiKey", "serviceId", "updatedAt") SELECT "createdAt", "defaultEmail", "defaultEmailFrom", "defaultPassword", "defaultUsername", "emailBackend", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", coalesce("emailSmtpUseSsl", false) AS "emailSmtpUseSsl", coalesce("emailSmtpUseTls", false) AS "emailSmtpUseTls", "emailSmtpUser", "enableOpenUserRegistration", "id", "mailgunApiKey", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "sendgridApiKey", "serviceId", "updatedAt" FROM "GlitchTip"; +DROP TABLE "GlitchTip"; +ALTER TABLE "new_GlitchTip" RENAME TO "GlitchTip"; +CREATE UNIQUE INDEX "GlitchTip_serviceId_key" ON "GlitchTip"("serviceId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221128105615_custom_sentry/migration.sql b/apps/server/prisma/migrations/20221128105615_custom_sentry/migration.sql new file mode 100644 index 000000000..00857eb0d --- /dev/null +++ b/apps/server/prisma/migrations/20221128105615_custom_sentry/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Setting" ADD COLUMN "sentryDSN" TEXT; diff --git a/apps/server/prisma/migrations/20221129081832_fix_defaults/migration.sql b/apps/server/prisma/migrations/20221129081832_fix_defaults/migration.sql new file mode 100644 index 000000000..96f6de3fb --- /dev/null +++ b/apps/server/prisma/migrations/20221129081832_fix_defaults/migration.sql @@ -0,0 +1,31 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "DNSServers" TEXT NOT NULL DEFAULT '1.1.1.1,8.8.8.8', + "ipv4" TEXT, + "ipv6" TEXT, + "arch" TEXT, + "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, + "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, + "proxyDefaultRedirect" TEXT, + "doNotTrack" BOOLEAN NOT NULL DEFAULT false, + "sentryDSN" TEXT, + "isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT true, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, + "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", "proxyDefaultRedirect", "sentryDSN", "updatedAt") SELECT coalesce("DNSServers", '1.1.1.1,8.8.8.8') AS "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "sentryDSN", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221129121702_preview_separator/migration.sql b/apps/server/prisma/migrations/20221129121702_preview_separator/migration.sql new file mode 100644 index 000000000..e0d64cdc8 --- /dev/null +++ b/apps/server/prisma/migrations/20221129121702_preview_separator/migration.sql @@ -0,0 +1,33 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "DNSServers" TEXT NOT NULL DEFAULT '1.1.1.1,8.8.8.8', + "ipv4" TEXT, + "ipv6" TEXT, + "arch" TEXT, + "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, + "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, + "numberOfDockerImagesKeptLocally" INTEGER NOT NULL DEFAULT 3, + "proxyDefaultRedirect" TEXT, + "doNotTrack" BOOLEAN NOT NULL DEFAULT false, + "sentryDSN" TEXT, + "previewSeparator" TEXT NOT NULL DEFAULT '.', + "isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT true, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, + "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", 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"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221129130036_keep_local_docker_images/migration.sql b/apps/server/prisma/migrations/20221129130036_keep_local_docker_images/migration.sql new file mode 100644 index 000000000..aa8136ed8 --- /dev/null +++ b/apps/server/prisma/migrations/20221129130036_keep_local_docker_images/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "gitCommitHash" TEXT; diff --git a/apps/server/prisma/migrations/20221130142058_reconfigure_docker_registries/migration.sql b/apps/server/prisma/migrations/20221130142058_reconfigure_docker_registries/migration.sql new file mode 100644 index 000000000..c77afb687 --- /dev/null +++ b/apps/server/prisma/migrations/20221130142058_reconfigure_docker_registries/migration.sql @@ -0,0 +1,66 @@ +/* + Warnings: + + - You are about to drop the column `isSystemWide` on the `DockerRegistry` table. All the data in the column will be lost. + +*/ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_DockerRegistry" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "url" TEXT NOT NULL, + "username" TEXT, + "password" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "teamId" TEXT, + CONSTRAINT "DockerRegistry_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_DockerRegistry" ("createdAt", "id", "name", "password", "teamId", "updatedAt", "url", "username") SELECT "createdAt", "id", "name", "password", "teamId", "updatedAt", "url", "username" FROM "DockerRegistry"; +DROP TABLE "DockerRegistry"; +ALTER TABLE "new_DockerRegistry" RENAME TO "DockerRegistry"; +CREATE TABLE "new_Application" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "fqdn" TEXT, + "repository" TEXT, + "configHash" TEXT, + "branch" TEXT, + "buildPack" TEXT, + "projectId" INTEGER, + "port" INTEGER, + "exposePort" INTEGER, + "installCommand" TEXT, + "buildCommand" TEXT, + "startCommand" TEXT, + "baseDirectory" TEXT, + "publishDirectory" TEXT, + "deploymentType" TEXT, + "phpModules" TEXT, + "pythonWSGI" TEXT, + "pythonModule" TEXT, + "pythonVariable" TEXT, + "dockerFileLocation" TEXT, + "denoMainFile" TEXT, + "denoOptions" TEXT, + "dockerComposeFile" TEXT, + "dockerComposeFileLocation" TEXT, + "dockerComposeConfiguration" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "destinationDockerId" TEXT, + "gitSourceId" TEXT, + "gitCommitHash" TEXT, + "baseImage" TEXT, + "baseBuildImage" TEXT, + "dockerRegistryId" TEXT, + CONSTRAINT "Application_gitSourceId_fkey" FOREIGN KEY ("gitSourceId") REFERENCES "GitSource" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "Application_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "Application_dockerRegistryId_fkey" FOREIGN KEY ("dockerRegistryId") REFERENCES "DockerRegistry" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_Application" ("baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "dockerRegistryId", "exposePort", "fqdn", "gitCommitHash", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt") SELECT "baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "dockerRegistryId", "exposePort", "fqdn", "gitCommitHash", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt" FROM "Application"; +DROP TABLE "Application"; +ALTER TABLE "new_Application" RENAME TO "Application"; +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/apps/server/prisma/migrations/20221201115801_simple_dockerfile_deployment/migration.sql b/apps/server/prisma/migrations/20221201115801_simple_dockerfile_deployment/migration.sql new file mode 100644 index 000000000..b3406e59e --- /dev/null +++ b/apps/server/prisma/migrations/20221201115801_simple_dockerfile_deployment/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "simpleDockerfile" TEXT; diff --git a/apps/server/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql b/apps/server/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql new file mode 100644 index 000000000..9f85d3518 --- /dev/null +++ b/apps/server/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "dockerRegistryImageName" TEXT; diff --git a/apps/server/prisma/migrations/migration_lock.toml b/apps/server/prisma/migrations/migration_lock.toml new file mode 100644 index 000000000..e5e5c4705 --- /dev/null +++ b/apps/server/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "sqlite" \ No newline at end of file diff --git a/apps/server/prisma/schema.prisma b/apps/server/prisma/schema.prisma new file mode 100644 index 000000000..54eefc504 --- /dev/null +++ b/apps/server/prisma/schema.prisma @@ -0,0 +1,705 @@ +generator client { + provider = "prisma-client-js" + binaryTargets = ["native"] +} + +datasource db { + provider = "sqlite" + url = env("COOLIFY_DATABASE_URL") +} + +model Certificate { + id String @id @default(cuid()) + key String + cert String + team Team? @relation(fields: [teamId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + teamId String? +} + +model Setting { + id String @id @default(cuid()) + fqdn String? @unique + dualCerts Boolean @default(false) + minPort Int @default(9000) + maxPort Int @default(9100) + DNSServers String @default("1.1.1.1,8.8.8.8") + ipv4 String? + ipv6 String? + arch String? + concurrentBuilds Int @default(1) + applicationStoragePathMigrationFinished Boolean @default(false) + numberOfDockerImagesKeptLocally Int @default(3) + proxyDefaultRedirect String? + doNotTrack Boolean @default(false) + sentryDSN String? + previewSeparator String @default(".") + isAPIDebuggingEnabled Boolean @default(false) + isRegistrationEnabled Boolean @default(true) + isAutoUpdateEnabled Boolean @default(false) + isDNSCheckEnabled Boolean @default(true) + isTraefikUsed Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model User { + id String @id @unique @default(cuid()) + email String @unique + type String + password String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + permission Permission[] + teams Team[] +} + +model Permission { + id String @id @default(cuid()) + userId String + teamId String + permission String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + team Team @relation(fields: [teamId], references: [id]) + user User @relation(fields: [userId], references: [id]) +} + +model Team { + id String @id @default(cuid()) + name String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + databaseId String? + serviceId String? + permissions Permission[] + sshKey SshKey[] + applications Application[] + database Database[] + destinationDocker DestinationDocker[] + gitSources GitSource[] + gitHubApps GithubApp[] + gitLabApps GitlabApp[] + service Service[] + users User[] + certificate Certificate[] + dockerRegistry DockerRegistry[] +} + +model TeamInvitation { + id String @id @default(cuid()) + uid String + email String + teamId String + teamName String + permission String + createdAt DateTime @default(now()) +} + +model Application { + id String @id @default(cuid()) + name String + fqdn String? + repository String? + configHash String? + branch String? + buildPack String? + projectId Int? + port Int? + exposePort Int? + installCommand String? + buildCommand String? + startCommand String? + baseDirectory String? + publishDirectory String? + deploymentType String? + phpModules String? + pythonWSGI String? + pythonModule String? + pythonVariable String? + dockerFileLocation String? + denoMainFile String? + denoOptions String? + dockerComposeFile String? + dockerComposeFileLocation String? + dockerComposeConfiguration String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + destinationDockerId String? + gitSourceId String? + gitCommitHash String? + baseImage String? + baseBuildImage String? + settings ApplicationSettings? + dockerRegistryId String? + dockerRegistryImageName String? + simpleDockerfile String? + + persistentStorage ApplicationPersistentStorage[] + secrets Secret[] + teams Team[] + connectedDatabase ApplicationConnectedDatabase? + previewApplication PreviewApplication[] + gitSource GitSource? @relation(fields: [gitSourceId], references: [id]) + destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id]) + dockerRegistry DockerRegistry? @relation(fields: [dockerRegistryId], references: [id]) +} + +model PreviewApplication { + id String @id @default(cuid()) + pullmergeRequestId String + sourceBranch String + isRandomDomain Boolean @default(false) + customDomain String? + applicationId String + application Application @relation(fields: [applicationId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ApplicationConnectedDatabase { + id String @id @default(cuid()) + applicationId String @unique + databaseId String? + hostedDatabaseType String? + hostedDatabaseHost String? + hostedDatabasePort Int? + hostedDatabaseName String? + hostedDatabaseUser String? + hostedDatabasePassword String? + hostedDatabaseDBName String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + database Database? @relation(fields: [databaseId], references: [id]) + application Application @relation(fields: [applicationId], references: [id]) +} + +model ApplicationSettings { + id String @id @default(cuid()) + applicationId String @unique + dualCerts Boolean @default(false) + debug Boolean @default(false) + previews Boolean @default(false) + autodeploy Boolean @default(true) + isBot Boolean @default(false) + isPublicRepository Boolean @default(false) + isDBBranching Boolean @default(false) + isCustomSSL Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + application Application @relation(fields: [applicationId], references: [id]) +} + +model ApplicationPersistentStorage { + id String @id @default(cuid()) + applicationId String + path String + oldPath Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + application Application @relation(fields: [applicationId], references: [id]) + + @@unique([applicationId, path]) +} + +model ServicePersistentStorage { + id String @id @default(cuid()) + serviceId String + path String + volumeName String? + predefined Boolean @default(false) + containerId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) + + @@unique([serviceId, containerId, path]) +} + +model Secret { + id String @id @default(cuid()) + name String + value String + isPRMRSecret Boolean @default(false) + isBuildSecret Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + applicationId String + application Application @relation(fields: [applicationId], references: [id]) + + @@unique([name, applicationId, isPRMRSecret]) +} + +model ServiceSecret { + id String @id @default(cuid()) + name String + value String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + serviceId String + service Service @relation(fields: [serviceId], references: [id]) + + @@unique([name, serviceId]) +} + +model BuildLog { + id String @id @default(cuid()) + applicationId String? + buildId String + line String + time BigInt +} + +model Build { + id String @id @default(cuid()) + type String + applicationId String? + destinationDockerId String? + gitSourceId String? + githubAppId String? + gitlabAppId String? + commit String? + pullmergeRequestId String? + previewApplicationId String? + forceRebuild Boolean @default(false) + sourceBranch String? + sourceRepository String? + branch String? + status String? @default("queued") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model DestinationDocker { + id String @id @default(cuid()) + network String @unique + name String + engine String? + remoteEngine Boolean @default(false) + remoteIpAddress String? + remoteUser String? + remotePort Int? + remoteVerified Boolean @default(false) + isCoolifyProxyUsed Boolean? @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + sshKeyId String? + sshKey SshKey? @relation(fields: [sshKeyId], references: [id]) + sshLocalPort Int? + application Application[] + database Database[] + service Service[] + teams Team[] +} + +model SshKey { + id String @id @default(cuid()) + name String + privateKey String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + teamId String? + team Team? @relation(fields: [teamId], references: [id]) + destinationDocker DestinationDocker[] +} + +model DockerRegistry { + id String @id @default(cuid()) + name String + url String + username String? + password String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + teamId String? + team Team? @relation(fields: [teamId], references: [id]) + application Application[] +} + +model GitSource { + id String @id @default(cuid()) + name String + forPublic Boolean @default(false) + type String? + apiUrl String? + htmlUrl String? + customPort Int @default(22) + organization String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + githubAppId String? @unique + gitlabAppId String? @unique + isSystemWide Boolean @default(false) + gitlabApp GitlabApp? @relation(fields: [gitlabAppId], references: [id]) + githubApp GithubApp? @relation(fields: [githubAppId], references: [id]) + application Application[] + teams Team[] +} + +model GithubApp { + id String @id @default(cuid()) + name String? @unique + appId Int? + installationId Int? + clientId String? + clientSecret String? + webhookSecret String? + privateKey String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + gitSource GitSource? + teams Team[] +} + +model GitlabApp { + id String @id @default(cuid()) + oauthId Int @unique + groupName String? @unique + deployKeyId Int? + privateSshKey String? + publicSshKey String? + webhookToken String? + appId String? + appSecret String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + gitSource GitSource? + teams Team[] +} + +model Database { + id String @id @default(cuid()) + name String + publicPort Int? + defaultDatabase String? + type String? + version String? + dbUser String? + dbUserPassword String? + rootUser String? + rootUserPassword String? + destinationDockerId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id]) + settings DatabaseSettings? + teams Team[] + applicationConnectedDatabase ApplicationConnectedDatabase[] + databaseSecret DatabaseSecret[] +} + +model DatabaseSecret { + id String @id @default(cuid()) + name String + value String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + databaseId String + database Database @relation(fields: [databaseId], references: [id]) + + @@unique([name, databaseId]) +} + +model DatabaseSettings { + id String @id @default(cuid()) + databaseId String @unique + isPublic Boolean @default(false) + appendOnly Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + database Database @relation(fields: [databaseId], references: [id]) +} + +model Service { + id String @id @default(cuid()) + name String + fqdn String? + exposePort Int? + dualCerts Boolean @default(false) + type String? + version String? + templateVersion String @default("0.0.0") + destinationDockerId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id]) + persistentStorage ServicePersistentStorage[] + serviceSecret ServiceSecret[] + serviceSetting ServiceSetting[] + teams Team[] + + fider Fider? + ghost Ghost? + glitchTip GlitchTip? + hasura Hasura? + meiliSearch MeiliSearch? + minio Minio? + moodle Moodle? + plausibleAnalytics PlausibleAnalytics? + umami Umami? + vscodeserver Vscodeserver? + wordpress Wordpress? + appwrite Appwrite? + searxng Searxng? + weblate Weblate? + taiga Taiga? +} + +model ServiceSetting { + id String @id @default(cuid()) + serviceId String + name String + value String + variableName String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) + + @@unique([serviceId, name]) +} + +model PlausibleAnalytics { + id String @id @default(cuid()) + email String? + username String? + password String + postgresqlUser String + postgresqlPassword String + postgresqlDatabase String + postgresqlPublicPort Int? + secretKeyBase String? + scriptName String @default("plausible.js") + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Minio { + id String @id @default(cuid()) + rootUser String + rootUserPassword String + publicPort Int? + apiFqdn String? + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Vscodeserver { + id String @id @default(cuid()) + password String + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Wordpress { + id String @id @default(cuid()) + extraConfig String? + tablePrefix String? + ownMysql Boolean @default(false) + mysqlHost String? + mysqlPort Int? + mysqlUser String? + mysqlPassword String? + mysqlRootUser String? + mysqlRootUserPassword String? + mysqlDatabase String? + mysqlPublicPort Int? + ftpEnabled Boolean @default(false) + ftpUser String? + ftpPassword String? + ftpPublicPort Int? + ftpHostKey String? + ftpHostKeyPrivate String? + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Ghost { + id String @id @default(cuid()) + defaultEmail String + defaultPassword String + mariadbUser String + mariadbPassword String + mariadbRootUser String + mariadbRootUserPassword String + mariadbDatabase String? + mariadbPublicPort Int? + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model MeiliSearch { + id String @id @default(cuid()) + masterKey String + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Umami { + id String @id @default(cuid()) + serviceId String @unique + postgresqlUser String + postgresqlPassword String + postgresqlDatabase String + postgresqlPublicPort Int? + umamiAdminPassword String + hashSalt String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Hasura { + id String @id @default(cuid()) + serviceId String @unique + postgresqlUser String + postgresqlPassword String + postgresqlDatabase String + postgresqlPublicPort Int? + graphQLAdminPassword String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Fider { + id String @id @default(cuid()) + serviceId String @unique + postgresqlUser String + postgresqlPassword String + postgresqlDatabase String + postgresqlPublicPort Int? + jwtSecret String + emailNoreply String? + emailMailgunApiKey String? + emailMailgunDomain String? + emailMailgunRegion String @default("EU") + emailSmtpHost String? + emailSmtpPort Int? + emailSmtpUser String? + emailSmtpPassword String? + emailSmtpEnableStartTls Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Moodle { + id String @id @default(cuid()) + serviceId String @unique + defaultUsername String + defaultPassword String + defaultEmail String + mariadbUser String + mariadbPassword String + mariadbRootUser String + mariadbRootUserPassword String + mariadbDatabase String + mariadbPublicPort Int? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Appwrite { + id String @id @default(cuid()) + serviceId String @unique + opensslKeyV1 String + executorSecret String + redisPassword String + mariadbHost String? + mariadbPort Int @default(3306) + mariadbUser String + mariadbPassword String + mariadbRootUser String + mariadbRootUserPassword String + mariadbDatabase String + mariadbPublicPort Int? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model GlitchTip { + id String @id @default(cuid()) + postgresqlUser String + postgresqlPassword String + postgresqlDatabase String + postgresqlPublicPort Int? + secretKeyBase String? + defaultEmail String + defaultUsername String + defaultPassword String + defaultEmailFrom String @default("glitchtip@domain.tdl") + emailSmtpHost String? @default("domain.tdl") + emailSmtpPort Int? @default(25) + emailSmtpUser String? + emailSmtpPassword String? + emailSmtpUseTls Boolean @default(false) + emailSmtpUseSsl Boolean @default(false) + emailBackend String? + mailgunApiKey String? + sendgridApiKey String? + enableOpenUserRegistration Boolean @default(true) + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Searxng { + id String @id @default(cuid()) + secretKey String + redisPassword String + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Weblate { + id String @id @default(cuid()) + adminPassword String + postgresqlHost String + postgresqlPort Int + postgresqlUser String + postgresqlPassword String + postgresqlDatabase String + postgresqlPublicPort Int? + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} + +model Taiga { + id String @id @default(cuid()) + secretKey String + erlangSecret String + djangoAdminPassword String + djangoAdminUser String + rabbitMQUser String + rabbitMQPassword String + postgresqlHost String + postgresqlPort Int + postgresqlUser String + postgresqlPassword String + postgresqlDatabase String + postgresqlPublicPort Int? + serviceId String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + service Service @relation(fields: [serviceId], references: [id]) +} diff --git a/apps/server/prisma/seed.js b/apps/server/prisma/seed.js new file mode 100644 index 000000000..3d4b16a28 --- /dev/null +++ b/apps/server/prisma/seed.js @@ -0,0 +1,113 @@ +const dotEnvExtended = require('dotenv-extended'); +dotEnvExtended.load(); +const crypto = require('crypto'); +const { PrismaClient } = require('@prisma/client'); +const prisma = new PrismaClient(); +const algorithm = 'aes-256-ctr'; + +async function main() { + // Enable registration for the first user + const settingsFound = await prisma.setting.findFirst({}); + if (!settingsFound) { + await prisma.setting.create({ + data: { + id: '0', + arch: process.arch, + } + }); + } else { + await prisma.setting.update({ + where: { + id: settingsFound.id + }, + data: { + id: '0' + } + }); + } + // Create local docker engine + const localDocker = await prisma.destinationDocker.findFirst({ + where: { engine: '/var/run/docker.sock' } + }); + if (!localDocker) { + await prisma.destinationDocker.create({ + data: { + engine: '/var/run/docker.sock', + name: 'Local Docker', + isCoolifyProxyUsed: true, + network: 'coolify' + } + }); + } + + // Set auto-update based on env variable + const isAutoUpdateEnabled = process.env['COOLIFY_AUTO_UPDATE'] === 'true'; + await prisma.setting.update({ + where: { + id: '0' + }, + data: { + isAutoUpdateEnabled + } + }); + // Create public github source + const github = await prisma.gitSource.findFirst({ + where: { htmlUrl: 'https://github.com', forPublic: true } + }); + if (!github) { + await prisma.gitSource.create({ + data: { + apiUrl: 'https://api.github.com', + htmlUrl: 'https://github.com', + forPublic: true, + name: 'Github Public', + type: 'github' + } + }); + } + // Create public gitlab source + const gitlab = await prisma.gitSource.findFirst({ + where: { htmlUrl: 'https://gitlab.com', forPublic: true } + }); + if (!gitlab) { + await prisma.gitSource.create({ + data: { + apiUrl: 'https://gitlab.com/api/v4', + htmlUrl: 'https://gitlab.com', + forPublic: true, + name: 'Gitlab Public', + type: 'gitlab' + } + }); + } + // Set new preview secrets + const secrets = await prisma.secret.findMany({ where: { isPRMRSecret: false } }) + if (secrets.length > 0) { + for (const secret of secrets) { + const previewSecrets = await prisma.secret.findMany({ where: { applicationId: secret.applicationId, name: secret.name, isPRMRSecret: true } }) + if (previewSecrets.length === 0) { + await prisma.secret.create({ data: { ...secret, id: undefined, isPRMRSecret: true } }) + } + } + } +} +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); + +const encrypt = (text) => { + if (text) { + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv(algorithm, process.env['COOLIFY_SECRET_KEY'], iv); + const encrypted = Buffer.concat([cipher.update(text), cipher.final()]); + return JSON.stringify({ + iv: iv.toString('hex'), + content: encrypted.toString('hex') + }); + } +}; \ No newline at end of file diff --git a/apps/server/src/api/index.ts b/apps/server/src/api/index.ts new file mode 100644 index 000000000..b47d8358c --- /dev/null +++ b/apps/server/src/api/index.ts @@ -0,0 +1,8 @@ +import type { FastifyPluginAsync } from 'fastify'; + +const root: FastifyPluginAsync = async (fastify): Promise => { + fastify.get('/', async function (_request, _reply) { + return { status: 'ok' }; + }); +}; +export default root; diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts new file mode 100644 index 000000000..e45c2a4bd --- /dev/null +++ b/apps/server/src/config.ts @@ -0,0 +1,7 @@ +import type { ServerOptions } from './server'; + +export const serverConfig: ServerOptions = { + dev: false, + port: 2022, + prefix: '/trpc' +}; diff --git a/apps/server/src/env.js b/apps/server/src/env.js new file mode 100644 index 000000000..fece0be52 --- /dev/null +++ b/apps/server/src/env.js @@ -0,0 +1,23 @@ +const dotenv = require('dotenv'); +// const isDev = process.env.NODE_ENV === 'development'; +// dotenv.config({ path: isDev ? '../../.env' : '.env' }); +dotenv.config(); +const { z } = require('zod'); + +/*eslint sort-keys: "error"*/ +const envSchema = z.object({ + CODESANDBOX_HOST: z.string().optional(), + NODE_ENV: z.enum(['development', 'test', 'production']), + COOLIFY_DATABASE_URL: z.string(), + COOLIFY_SECRET_KEY: z.string().length(32), + COOLIFY_WHITE_LABELED: z.string().optional(), + COOLIFY_WHITE_LABELED_ICON: z.string().optional() +}); + +const env = envSchema.safeParse(process.env); + +if (!env.success) { + console.error('❌ Invalid environment variables:', JSON.stringify(env.error.format(), null, 4)); + process.exit(1); +} +module.exports.env = env.data; diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts new file mode 100644 index 000000000..8f2d2782e --- /dev/null +++ b/apps/server/src/index.ts @@ -0,0 +1,7 @@ +import { serverConfig } from './config'; +import { createServer } from './server'; + +const server = createServer(serverConfig); + +server.start(); + diff --git a/apps/server/src/lib/common.ts b/apps/server/src/lib/common.ts new file mode 100644 index 000000000..bebbf6a49 --- /dev/null +++ b/apps/server/src/lib/common.ts @@ -0,0 +1,183 @@ +import type { Permission, Setting, Team, TeamInvitation, User } from '@prisma/client'; +import { prisma } from '../prisma'; +import bcrypt from 'bcryptjs'; +import crypto from 'crypto'; +import fs from 'fs/promises'; +import { uniqueNamesGenerator, adjectives, colors, animals } from 'unique-names-generator'; +import type { Config } from 'unique-names-generator'; +import { env } from '../env'; +import { day } from './dayjs'; +import { executeCommand } from './executeCommand'; + +const customConfig: Config = { + dictionaries: [adjectives, colors, animals], + style: 'capital', + separator: ' ', + length: 3 +}; +const algorithm = 'aes-256-ctr'; +export const isDev = env.NODE_ENV === 'development'; +export const version = '3.13.0'; +export const sentryDSN = + 'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216'; + +export async function listSettings(): Promise { + return await prisma.setting.findUnique({ where: { id: '0' } }); +} +export async function getCurrentUser( + userId: string +): Promise<(User & { permission: Permission[]; teams: Team[] }) | null> { + return await prisma.user.findUnique({ + where: { id: userId }, + include: { teams: true, permission: true } + }); +} +export async function getTeamInvitation(userId: string): Promise { + return await prisma.teamInvitation.findMany({ where: { uid: userId } }); +} + +export async function hashPassword(password: string): Promise { + const saltRounds = 15; + return bcrypt.hash(password, saltRounds); +} +export async function comparePassword(password: string, hashedPassword: string): Promise { + return bcrypt.compare(password, hashedPassword); +} +export const uniqueName = (): string => uniqueNamesGenerator(customConfig); + +export const decrypt = (hashString: string) => { + if (hashString) { + try { + const hash = JSON.parse(hashString); + const decipher = crypto.createDecipheriv( + algorithm, + env.COOLIFY_SECRET_KEY, + Buffer.from(hash.iv, 'hex') + ); + const decrpyted = Buffer.concat([ + decipher.update(Buffer.from(hash.content, 'hex')), + decipher.final() + ]); + return decrpyted.toString(); + } catch (error) { + if (error instanceof Error) { + console.log({ decryptionError: error.message }); + } + return hashString; + } + } + return false; +}; + +export function generateRangeArray(start: number, end: number) { + return Array.from({ length: end - start }, (_v, k) => k + start); +} +export function generateTimestamp(): string { + return `${day().format('HH:mm:ss.SSS')}`; +} +export const encrypt = (text: string) => { + if (text) { + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv(algorithm, env.COOLIFY_SECRET_KEY, iv); + const encrypted = Buffer.concat([cipher.update(text.trim()), cipher.final()]); + return JSON.stringify({ + iv: iv.toString('hex'), + content: encrypted.toString('hex') + }); + } + return false; +}; + +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: { arch: string }) => d.arch !== 'amd64'); + } + return jsonData; + } catch (error) { + return []; + } finally { + await open?.close(); + } +} +export function isARM(arch: string) { + if (arch === 'arm' || arch === 'arm64' || arch === 'aarch' || arch === 'aarch64') { + return true; + } + return false; +} + +export async function removeService({ id }: { id: string }): Promise { + await prisma.serviceSecret.deleteMany({ where: { serviceId: id } }); + await prisma.serviceSetting.deleteMany({ where: { serviceId: id } }); + await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } }); + await prisma.meiliSearch.deleteMany({ where: { serviceId: id } }); + await prisma.fider.deleteMany({ where: { serviceId: id } }); + await prisma.ghost.deleteMany({ where: { serviceId: id } }); + await prisma.umami.deleteMany({ where: { serviceId: id } }); + await prisma.hasura.deleteMany({ where: { serviceId: id } }); + await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } }); + await prisma.minio.deleteMany({ where: { serviceId: id } }); + await prisma.vscodeserver.deleteMany({ where: { serviceId: id } }); + await prisma.wordpress.deleteMany({ where: { serviceId: id } }); + await prisma.glitchTip.deleteMany({ where: { serviceId: id } }); + await prisma.moodle.deleteMany({ where: { serviceId: id } }); + await prisma.appwrite.deleteMany({ where: { serviceId: id } }); + await prisma.searxng.deleteMany({ where: { serviceId: id } }); + await prisma.weblate.deleteMany({ where: { serviceId: id } }); + await prisma.taiga.deleteMany({ where: { serviceId: id } }); + + await prisma.service.delete({ where: { id } }); +} + +export const createDirectories = async ({ + repository, + buildId +}: { + repository: string; + buildId: string; +}): Promise<{ workdir: string; repodir: string }> => { + 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) {} + if (workdirFound) { + await executeCommand({ command: `rm -fr ${workdir}` }); + } + await executeCommand({ command: `mkdir -p ${workdir}` }); + return { + workdir, + repodir + }; +}; + +export async function saveDockerRegistryCredentials({ url, username, password, workdir }) { + if (!username || !password) { + return null; + } + + let decryptedPassword = decrypt(password); + const location = `${workdir}/.docker`; + + try { + await fs.mkdir(`${workdir}/.docker`); + } catch (error) { + console.log(error); + } + const payload = JSON.stringify({ + auths: { + [url]: { + auth: Buffer.from(`${username}:${decryptedPassword}`).toString('base64') + } + } + }); + await fs.writeFile(`${location}/config.json`, payload); + return location; +} diff --git a/apps/server/src/lib/dayjs.ts b/apps/server/src/lib/dayjs.ts new file mode 100644 index 000000000..9ff5b0a1a --- /dev/null +++ b/apps/server/src/lib/dayjs.ts @@ -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 }; diff --git a/apps/server/src/lib/docker.ts b/apps/server/src/lib/docker.ts new file mode 100644 index 000000000..0f02da0e2 --- /dev/null +++ b/apps/server/src/lib/docker.ts @@ -0,0 +1,157 @@ +import { executeCommand } from './executeCommand'; + +export async function checkContainer({ + dockerId, + container, + remove = false +}: { + dockerId: string; + container: string; + remove?: boolean; +}): Promise<{ + found: boolean; + status?: { isExited: boolean; isRunning: boolean; isRestarting: boolean }; +}> { + let containerFound = false; + try { + const { stdout } = await executeCommand({ + dockerId, + command: `docker inspect --format '{{json .State}}' ${container}` + }); + containerFound = true; + const parsedStdout = JSON.parse(stdout); + const status = parsedStdout.Status; + const isRunning = status === 'running'; + const isRestarting = status === 'restarting'; + const isExited = status === 'exited'; + if (status === 'created') { + await executeCommand({ + dockerId, + command: `docker rm ${container}` + }); + } + if (remove && status === 'exited') { + await executeCommand({ + dockerId, + command: `docker rm ${container}` + }); + } + + return { + found: containerFound, + status: { + isRunning, + isRestarting, + isExited + } + }; + } catch (err) { + // Container not found + } + return { + found: false + }; +} + +export async function removeContainer({ + id, + dockerId +}: { + id: string; + dockerId: string; +}): Promise { + try { + const { stdout } = await executeCommand({ + dockerId, + command: `docker inspect --format '{{json .State}}' ${id}` + }); + if (JSON.parse(stdout).Running) { + await executeCommand({ dockerId, command: `docker stop -t 0 ${id}` }); + await executeCommand({ dockerId, command: `docker rm ${id}` }); + } + if (JSON.parse(stdout).Status === 'exited') { + await executeCommand({ dockerId, command: `docker rm ${id}` }); + } + } catch (error) { + throw error; + } +} + +export async function stopDatabaseContainer(database: any): Promise { + let everStarted = false; + const { + id, + destinationDockerId, + destinationDocker: { engine, id: dockerId } + } = database; + if (destinationDockerId) { + try { + const { stdout } = await executeCommand({ + dockerId, + command: `docker inspect --format '{{json .State}}' ${id}` + }); + + if (stdout) { + everStarted = true; + await removeContainer({ id, dockerId }); + } + } catch (error) { + // + } + } + return everStarted; +} +export async function stopTcpHttpProxy( + id: string, + destinationDocker: any, + publicPort: number, + forceName: string | null = null +): Promise<{ stdout: string; stderr: string } | Error | unknown> { + const { id: dockerId } = destinationDocker; + let container = `${id}-${publicPort}`; + if (forceName) container = forceName; + const { found } = await checkContainer({ dockerId, container }); + try { + if (!found) return true; + return await executeCommand({ + dockerId, + command: `docker stop -t 0 ${container} && docker rm ${container}`, + shell: true + }); + } catch (error) { + return error; + } +} + +export function formatLabelsOnDocker(data: any) { + return data + .trim() + .split('\n') + .map((a) => JSON.parse(a)) + .map((container) => { + const labels = container.Labels.split(','); + let jsonLabels = {}; + labels.forEach((l) => { + const name = l.split('=')[0]; + const value = l.split('=')[1]; + jsonLabels = { ...jsonLabels, ...{ [name]: value } }; + }); + container.Labels = jsonLabels; + return container; + }); +} + +export function defaultComposeConfiguration(network: string): any { + return { + networks: [network], + restart: 'on-failure', + deploy: { + restart_policy: { + condition: 'on-failure', + delay: '5s', + max_attempts: 10, + window: '120s' + } + } + }; +} diff --git a/apps/server/src/lib/executeCommand.ts b/apps/server/src/lib/executeCommand.ts new file mode 100644 index 000000000..31119be08 --- /dev/null +++ b/apps/server/src/lib/executeCommand.ts @@ -0,0 +1,196 @@ +import { prisma } from '../prisma'; +import os from 'os'; +import fs from 'fs/promises'; +import type { ExecaChildProcess } from 'execa'; +import sshConfig from 'ssh-config'; + +import { getFreeSSHLocalPort } from './ssh'; +import { env } from '../env'; +import { BuildLog, saveBuildLog } from './logging'; +import { decrypt } from './common'; + +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 | null; + buildId?: string; + applicationId?: string; + debug?: boolean; +}): Promise> { + 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 && dockerCommand && dockerArgs) { + const destinationDocker = await prisma.destinationDocker.findUnique({ + where: { id: dockerId } + }); + if (!destinationDocker) { + throw new Error('Destination docker not found'); + } + let { remoteEngine, remoteIpAddress, engine } = destinationDocker; + if (remoteEngine) { + await createRemoteEngineConfiguration(dockerId); + engine = `ssh://${remoteIpAddress}-remote`; + } else { + engine = 'unix:///var/run/docker.sock'; + } + + if (env.CODESANDBOX_HOST) { + if (command.startsWith('docker compose')) { + command = command.replace(/docker compose/gi, 'docker-compose'); + } + } + if (sshCommand) { + if (shell) { + return execaCommand(`ssh ${remoteIpAddress}-remote ${command}`); + } + //@ts-ignore + return await execa('ssh', [`${remoteIpAddress}-remote`, dockerCommand, ...dockerArgs]); + } + if (stream) { + return await new Promise(async (resolve, reject) => { + let subprocess = null; + if (shell) { + //@ts-ignore + subprocess = execaCommand(command, { + env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } + }); + } else { + //@ts-ignore + subprocess = execa(dockerCommand, dockerArgs, { + env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } + }); + } + const logs: any[] = []; + if (subprocess && subprocess.stdout && subprocess.stderr) { + subprocess.stdout.on('data', async (data: string) => { + const stdout = data.toString(); + const array = stdout.split('\n'); + for (const line of array) { + if (line !== '\n' && line !== '') { + const log: BuildLog = { + line: `${line.replace('\n', '')}`, + buildId, + applicationId + }; + logs.push(log); + if (debug) { + await saveBuildLog(log); + } + } + } + }); + subprocess.stderr.on('data', async (data: string) => { + const stderr = data.toString(); + const array = stderr.split('\n'); + for (const line of array) { + if (line !== '\n' && line !== '') { + const log = { + line: `${line.replace('\n', '')}`, + buildId, + applicationId + }; + logs.push(log); + if (debug) { + await saveBuildLog(log); + } + } + } + }); + subprocess.on('exit', async (code: number) => { + if (code === 0) { + //@ts-ignore + resolve(code); + } else { + if (!debug) { + for (const log of logs) { + await saveBuildLog(log); + } + } + reject(code); + } + }); + } + }); + } else { + if (shell) { + return await execaCommand(command, { + //@ts-ignore + env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } + }); + } else { + //@ts-ignore + return await execa(dockerCommand, dockerArgs, { + env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } + }); + } + } + } else { + if (shell) { + return execaCommand(command, { shell: true }); + } + //@ts-ignore + return await execa(dockerCommand, dockerArgs); + } +} + +export async function createRemoteEngineConfiguration(id: string) { + const homedir = os.homedir(); + const sshKeyFile = `/tmp/id_rsa-${id}`; + const localPort = await getFreeSSHLocalPort(id); + const { + sshKey: { privateKey }, + network, + remoteIpAddress, + remotePort, + remoteUser + } = 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`; + + 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) {} + + const found = config.find({ Host }); + const foundIp = config.find({ Host: remoteIpAddress }); + + if (found) config.remove({ Host }); + if (foundIp) config.remove({ Host: remoteIpAddress }); + + config.append({ + Host, + Hostname: remoteIpAddress, + Port: remotePort.toString(), + User: remoteUser, + StrictHostKeyChecking: 'no', + IdentityFile: sshKeyFile, + ControlMaster: 'auto', + ControlPath: `${homedir}/.ssh/coolify-${remoteIpAddress}-%r@%h:%p`, + ControlPersist: '10m' + }); + + try { + await fs.stat(`${homedir}/.ssh/`); + } catch (error) { + await fs.mkdir(`${homedir}/.ssh/`); + } + return await fs.writeFile(`${homedir}/.ssh/config`, sshConfig.stringify(config)); +} diff --git a/apps/server/src/lib/logging.ts b/apps/server/src/lib/logging.ts new file mode 100644 index 000000000..bb4dde7a0 --- /dev/null +++ b/apps/server/src/lib/logging.ts @@ -0,0 +1,48 @@ +import { prisma } from '../prisma'; +import { encrypt, generateTimestamp, isDev } from './common'; +import { day } from './dayjs'; + +export type Line = string | { shortMessage: string; stderr: string }; +export type BuildLog = { + line: Line; + buildId?: string; + applicationId?: string; +}; +export const saveBuildLog = async ({ line, buildId, applicationId }: BuildLog): Promise => { + if (buildId === 'undefined' || buildId === 'null' || !buildId) return; + if (applicationId === 'undefined' || applicationId === 'null' || !applicationId) return; + const { default: got } = await import('got'); + if (typeof line === 'object' && line) { + if (line.shortMessage) { + line = line.shortMessage + '\n' + line.stderr; + } else { + line = JSON.stringify(line); + } + } + if (line && typeof line === 'string' && line.includes('ghs_')) { + const regex = /ghs_.*@/g; + line = line.replace(regex, '@'); + } + const addTimestamp = `[${generateTimestamp()}] ${line}`; + const fluentBitUrl = isDev ? 'http://localhost:24224' : 'http://coolify-fluentbit:24224'; + + if (isDev) { + console.debug(`[${applicationId}] ${addTimestamp}`); + } + try { + return await got.post(`${fluentBitUrl}/${applicationId}_buildlog_${buildId}.csv`, { + json: { + line: encrypt(line) + } + }); + } catch (error) { + return await prisma.buildLog.create({ + data: { + line: addTimestamp, + buildId, + time: Number(day().valueOf()), + applicationId + } + }); + } +}; diff --git a/apps/server/src/lib/ssh.ts b/apps/server/src/lib/ssh.ts new file mode 100644 index 000000000..77cbf7307 --- /dev/null +++ b/apps/server/src/lib/ssh.ts @@ -0,0 +1,47 @@ +import { prisma } from '../prisma'; +import { generateRangeArray } from './common'; + +export async function getFreeSSHLocalPort(id: string): Promise { + const { default: isReachable } = await import('is-port-reachable'); + const { remoteIpAddress, sshLocalPort } = await prisma.destinationDocker.findUnique({ + where: { id } + }); + if (sshLocalPort) { + return Number(sshLocalPort); + } + + const data = await prisma.setting.findFirst(); + const { minPort, maxPort } = data; + + const ports = await prisma.destinationDocker.findMany({ + where: { sshLocalPort: { not: null }, remoteIpAddress: { not: remoteIpAddress } } + }); + + const alreadyConfigured = await prisma.destinationDocker.findFirst({ + where: { + remoteIpAddress, + id: { not: id }, + sshLocalPort: { not: null } + } + }); + if (alreadyConfigured?.sshLocalPort) { + await prisma.destinationDocker.update({ + where: { id }, + data: { sshLocalPort: alreadyConfigured.sshLocalPort } + }); + return Number(alreadyConfigured.sshLocalPort); + } + const range = generateRangeArray(minPort, maxPort); + const availablePorts = range.filter((port) => !ports.map((p) => p.sshLocalPort).includes(port)); + for (const port of availablePorts) { + const found = await isReachable(port, { host: 'localhost' }); + if (!found) { + await prisma.destinationDocker.update({ + where: { id }, + data: { sshLocalPort: Number(port) } + }); + return Number(port); + } + } + return false; +} diff --git a/apps/server/src/prisma.ts b/apps/server/src/prisma.ts new file mode 100644 index 000000000..6aa0585c4 --- /dev/null +++ b/apps/server/src/prisma.ts @@ -0,0 +1,20 @@ +/** + * Instantiates a single instance PrismaClient and save it on the global object. + * @link https://www.prisma.io/docs/support/help-articles/nextjs-prisma-client-dev-practices + */ +import { env } from './env'; +import { PrismaClient } from '@prisma/client'; + +const prismaGlobal = global as typeof global & { + prisma?: PrismaClient; +}; + +export const prisma: PrismaClient = + prismaGlobal.prisma || + new PrismaClient({ + log: env.NODE_ENV !== 'development' ? ['query', 'error', 'warn'] : ['error'] + }); + +if (env.NODE_ENV !== 'production') { + prismaGlobal.prisma = prisma; +} diff --git a/apps/server/src/server.ts b/apps/server/src/server.ts new file mode 100644 index 000000000..9a8748fe3 --- /dev/null +++ b/apps/server/src/server.ts @@ -0,0 +1,70 @@ +import { fastifyTRPCPlugin } from '@trpc/server/adapters/fastify'; +import fastify from 'fastify'; +import { appRouter } from './trpc'; +import { createContext } from './trpc/context'; +import cors from '@fastify/cors'; +import * as path from 'node:path'; +import serve from '@fastify/static'; +import autoLoad from '@fastify/autoload'; +// import { prisma } from './prisma'; + +const isDev = process.env['NODE_ENV'] === 'development'; + +export interface ServerOptions { + dev?: boolean; + port?: number; + prefix?: string; +} + +export function createServer(opts: ServerOptions) { + const dev = opts.dev ?? true; + const port = opts.port ?? 3000; + const prefix = opts.prefix ?? '/trpc'; + const server = fastify({ logger: dev, trustProxy: true }); + server.register(cors); + server.register(fastifyTRPCPlugin, { + prefix, + trpcOptions: { + router: appRouter, + createContext, + onError({ error, type, path, input, ctx, req }) { + console.error('Error:', error); + if (error.code === 'INTERNAL_SERVER_ERROR') { + // send to bug reporting + } + } + } + }); + // Serve static files in production. Static files are generated by `yarn build` in the client folder by SvelteKit. + if (!isDev) { + server.register(serve, { + root: path.join(__dirname, './public'), + preCompressed: true + }); + server.setNotFoundHandler(async function (request, reply) { + if (request.raw.url && request.raw.url.startsWith('/api')) { + return reply.status(404).send({ + success: false + }); + } + return reply.status(200).sendFile('index.html'); + }); + } + server.register(autoLoad, { + dir: path.join(__dirname, 'api'), + options: { prefix: '/api' } + }); + + const stop = () => server.close(); + const start = async () => { + try { + await server.listen({ host: '0.0.0.0', port }); + console.log('Coolify server is listening on port', port, 'at 0.0.0.0 🚀'); + } catch (err) { + server.log.error(err); + process.exit(1); + } + }; + + return { server, start, stop }; +} diff --git a/apps/server/src/tags.json b/apps/server/src/tags.json new file mode 100644 index 000000000..0d84f6caa --- /dev/null +++ b/apps/server/src/tags.json @@ -0,0 +1 @@ +[{"name":"appsmith","image":"appsmith/appsmith-ce","tags":["v1.8.8","v1.8.6","v1.8.4","v1.8.2","v1.8.10","v1.8.0","v1.7.8","v1.7.6","v1.7.4","v1.7.2","v1.7.13","v1.7.11","v1.7.1","v1.6.9","v1.6.7","v1.6.5","v1.6.3","v1.6.22","v1.6.20","v1.6.19","v1.6.17","v1.6.15","v1.6.13","v1.6.11","v1.6.1","v1.5.30","v1.5.28","v1.5.26","v1.5.24","v1.5.22"]},{"name":"appwrite","image":"appwrite/appwrite","tags":["1.1.2","1.1.0","1.0.3","1.0.1","1.0.0","0.9.3","0.9.1","0.8.0","0.7.1","0.6.2","0.6.0","0.5.2","0.5.0","0.3.1","0.2.0","0.15.2","0.15.0","0.14.2","0.14.0","0.13.4","0.13.2","0.13.0","0.12.3","0.12.1","0.12.0","0.11.2","0.11.0","0.10.4","0.10.2","0.10.0"]},{"name":"fider","image":"getfider/fider","tags":["stable","master","main","dev","SHA_ee6e83cfaadadaa56ab76e089e01f5631af3506f","SHA_deb4f9b4f561d890d8a80e6872fea9a98a265cc6","SHA_d5cc307909d43447200483d76b5db74d8ed8349e","SHA_d1674476577a7fd3c88fc29f91c3f35f5bd6a260","SHA_d107cbb157abca6576110080736213efe0955cff","SHA_c9c55b2f5b33a76015241b97e03cfac1254b42a7","SHA_bcf451a3cb02d5c8a489fd30309249296057b084","SHA_bbfe419639514f949a042807addf0fde7d4de225","SHA_adc3afc4c7bcf96931a5f90cab65c282d860dbfd","SHA_ab5283ae95334f10b5041402dce79e333c472015","SHA_a3f4cb5ed0a4ee2d726705fc426636364aac17a1","SHA_a18224142bf51bc6463c3d22f45f62287902e9a6","SHA_8e5cff30d95963eaee2587488d351e0d658c8195","SHA_8cabe2817ce7ccaf2f0a9fdbb1b5d3411de87f81","SHA_7851f9da566132d87fa2a63004e78c3bc9c09c6c","SHA_6c0f2bed1754e9d579eb9575129a6e3dbc529c32","SHA_603508c8790d6a6fb1e852df1a58ead8e5b3ea6c","SHA_55efacf164a4749b50ee68ae8925e7dc9dfa3a0c","SHA_4bdd291ce61e5f5dfc063fa1b2d9be8c9ff1d4c4","SHA_3fba9cb6a9ceab0c78c6cff3220610f591f657cb","SHA_3d635b57606a9885babe91fe975b11429e0f2c38","SHA_3b794edbd9789a8aa38ecd3714bc536a675d3058","SHA_3570c454ad3252b690608f7bf8051737d8519f8a","SHA_263e2709fd145f3ea511e5557e170102899995b0","SHA_255c30ed012fc4c39ffc97efc1d3b00425b17c72","SHA_17f92b16ef790003338f0926fc8d791a9a61333c"]},{"name":"ghost-mariadb","image":"bitnami/ghost","tags":["5.7.1","5.7.0","5.5.0","5.4.1","5.4.0","5.3.1","5.3.0","5.24.2","5.24.1","5.24.0","5.23.0","5.22.9","5.22.8","5.22.7","5.22.6","5.22.5","5.22.4","5.22.3","5.22.2","5.22.11","5.22.10","5.22.1","5.22.0","5.21.0","5.20.0","5.2.4","5.2.3","5.2.2","5.2.1","5.19.3","4.48.8"]},{"name":"ghost-mysql","image":"library/ghost","tags":["5.9.4","5.8.3","5.8.2","5.7.1","5.7.0","5.5.0","5.4.1","5.3.1","5.3.0","5.24.2","5.24.1","5.23.0","5.22.9","5.22.8","5.22.4","5.22.11","5.22.10","5.22.1","5.20.0","5.2.4","5.2.3","5.2.2","5.2.1","5.19.3","5.19.0","5.18.0","5.17.2","5.17.1","5.17.0","5.16.2"]},{"name":"ghost-only","image":"library/ghost","tags":["5.9.4","5.8.3","5.8.2","5.7.1","5.7.0","5.5.0","5.4.1","5.3.1","5.3.0","5.24.2","5.24.1","5.23.0","5.22.9","5.22.8","5.22.4","5.22.11","5.22.10","5.22.1","5.20.0","5.2.4","5.2.3","5.2.2","5.2.1","5.19.3","5.19.0","5.18.0","5.17.2","5.17.1","5.17.0","5.16.2"]},{"name":"gitea","image":"gitea/gitea","tags":["1.9.6","1.9.5","1.9.4","1.9.3","1.9.2","1.9.0","1.8.3","1.8.1","1.8.0","1.7.5","1.7.3","1.7.1","1.7.0","1.6.3","1.6.1","1.6.0","1.5.3","1.5.1","1.5.0","1.4.3","1.4.1","1.4.0","1.3.3","1.3.1","1.3.0","1.2.3","1.2.1","1.2.0","1.17.3","1.17.2"]},{"name":"glitchtip","image":"glitchtip/glitchtip","tags":["v2.0.7","v2.0.5","v2.0.2","v2.0.0","v1.9.2","v1.9.0","v1.8.4","v1.8.2","v1.8.0","v1.7.1","v1.6.4","v1.6.2","v1.6.0","v1.5.3","v1.5.1","v1.4.1","v1.3.3","v1.3.1","v1.2.6","v1.2.4","v1.2.2","v1.2.0","v1.12.4","v1.12.2","v1.12.0","v1.10.3","v1.10.1","v1.1.2","v1.1.0","v1.0.8"]},{"name":"grafana","image":"grafana/grafana","tags":["9.3.1","9.3.0","9.2.7","9.2.6","9.2.5","9.2.4","9.2.3","9.2.2","9.2.1","9.2.0","9.1.8","9.1.7","9.1.6","9.1.5","9.1.4","9.1.3","9.1.2","9.1.1","9.1.0","9.0.9","9.0.8","9.0.7","9.0.6","9.0.5","9.0.4","9.0.3","9.0.2","9.0.1","9.0.0","8.5.9"]},{"name":"hasura","image":"hasura/graphql-engine","tags":["v2.9.0","v2.8.4","v2.8.3","v2.8.2","v2.8.1","v2.8.0","v2.7.0","v2.6.2","v2.6.1","v2.6.0","v2.5.2","v2.5.1","v2.5.0","v2.4.0","v2.3.1","v2.3.0","v2.2.2","v2.2.1","v2.2.0","v2.15.2","v2.15.1","v2.15.0","v2.14.1","v2.14.0","v2.13.2","v2.13.1","v2.13.0","v2.12.1","v2.12.0","v2.11.3"]},{"name":"keycloak","image":"quay.io/keycloak/keycloak","tags":["9.0.3","9.0.0","8.0.1","7.0.0","6.0.1","6.0.0","20.0.1","20.0.0","19.0.3","19.0.1","19.0.0","18.0.1","18.0.0","17.0.1","17.0.0","16.1.0","15.1.1","15.0.2","15.0.0","13.0.1","12.0.4","12.0.2","12.0.0","11.0.2","11.0.0","10.0.1"]},{"name":"languagetool","image":"silviof/docker-languagetool","tags":["latest","5.8","5.7","5.6","5.5","5.4","5.3"]},{"name":"lavalink","image":"fredboat/lavalink","tags":["v3.7","v3.6","v3-vda0b3a4b3916a7b1a2b79702de1143c3a6939810-SNAPSHOT","v3-vc92690c425390bd20f6c51643c67ba79ab85b7e0-SNAPSHOT","v3-vab81dcd46adf3e8a961dd57eacd2a1bde1233e6c-SNAPSHOT","v3-v9c9432704d6a4badfcbd06a57597c54bed8f4326-SNAPSHOT","v3-v3.0","v3-v3","v3-v124f8fae7dab299f9cdf1cb4c1715be455497286-SNAPSHOT","v3-","v3","v2.0.1","v2.0","v2","update-udpqueue-vb4a439d6147dbd8641ea4f265e8efc9f1e16e2d3-SNAPSHOT","update-udpqueue-","update-udpqueue","revert-713-fix-error-for-loading-jda-nas","refactor-github-actions","patch-update-github-actions","patch-more-configurable-github-actions","patch-lavaplayer-update","patch-lavaplayer-bump","patch-build-number","next-api-vd4db194cac7a839a3899857f1f6d7b910369309d-SNAPSHOT","next-api-vc2e018d5ffef54b2d17244b3d213e31723a084d6-SNAPSHOT","next-api-v42cb5f7c58e98d1911e87bffb35aee0a235b85f8-SNAPSHOT","next-api-v31a243bda80badbd7d643f68fc1f87e99639060f-SNAPSHOT","next-api-v17f6884434c2d70d1704b2322a951d9f07af8865-SNAPSHOT","next-api-"]},{"name":"meilisearch","image":"getmeili/meilisearch","tags":["v0.9.0","v0.8.3","v0.8.1","v0.30.0","v0.29.1","v0.29.0","v0.28.1","v0.28.0","v0.27.1","v0.27.0","v0.26.1","v0.26.0","v0.25.1","v0.25.0","v0.23.1","v0.23.0","v0.21.1","v0.21.0","v0.20.0","v0.19.0","v0.18.1","v0.18.0","v0.17.0","v0.16.0","v0.14.1","v0.14.0","v0.12.0","v0.11.0","v0.10.0","0.14.1"]},{"name":"minio","image":"minio/minio","tags":["RELEASE.2022-11-29T23-40-49Z.fips","RELEASE.2022-11-26T22-43-32Z.fips","RELEASE.2022-11-17T23-20-09Z.fips","RELEASE.2022-11-11T03-44-20Z.fips","RELEASE.2022-11-10T18-20-21Z.fips","RELEASE.2022-11-08T05-27-07Z.fips","RELEASE.2022-10-29T06-21-33Z.fips","RELEASE.2022-10-24T18-35-07Z.hotfix.ce525fdaf","RELEASE.2022-10-24T18-35-07Z.fips","RELEASE.2022-10-21T22-37-48Z.fips","RELEASE.2022-10-20T00-55-09Z.fips","RELEASE.2022-10-15T19-57-03Z.fips","RELEASE.2022-10-08T20-11-00Z.fips","RELEASE.2022-10-05T14-58-27Z.fips","RELEASE.2022-10-02T19-29-29Z.fips","RELEASE.2022-09-25T15-44-53Z.fips","RELEASE.2022-09-22T18-57-27Z.fips","RELEASE.2022-09-17T00-09-45Z.hotfix.fc6d6fdbd","RELEASE.2022-09-17T00-09-45Z.hotfix.4bb22d5cd","RELEASE.2022-09-17T00-09-45Z","RELEASE.2022-09-07T22-25-02Z","RELEASE.2022-09-01T23-53-36Z","RELEASE.2022-08-26T19-53-15Z","RELEASE.2022-08-25T07-17-05Z","RELEASE.2022-08-22T23-53-06Z.fips","RELEASE.2022-08-13T21-54-44Z.fips","RELEASE.2022-08-11T04-37-28Z.fips","RELEASE.2022-08-08T18-34-09Z.fips","RELEASE.2022-08-05T23-27-09Z.fips","RELEASE.2022-08-02T23-59-16Z.fips"]},{"name":"n8n","image":"n8nio/n8n","tags":["0.99.1","0.99.0","0.98.0","0.97.0","0.96.0","0.95.1","0.95.0","0.94.1","0.94.0","0.93.0","0.92.0","0.91.0","0.9.0","0.89.2","0.88.1","0.88.0","0.87.2","0.87.1","0.87.0","0.86.1","0.86.0","0.85.0","0.84.4","0.84.3","0.84.1","0.84.0","0.83.0","0.82.1","0.82.0","0.81.0"]},{"name":"nocodb","image":"nocodb/nocodb","tags":["0.99.2","0.99.0","0.98.3","0.98.1","0.97.0","0.96.3","0.96.1","0.92.4","0.92.0","0.91.8","0.91.6","0.91.1","0.90.8","0.90.5","0.90.3","0.90.11","0.90.1","0.9.9","0.9.7","0.9.43","0.9.41","0.9.39","0.9.37","0.9.35","0.9.33","0.9.31","0.9.29","0.9.27","0.9.25","0.9.22"]},{"name":"plausibleanalytics-arm","image":"plausible/analytics","tags":["v1.5.0-rc.2","v1.5.0-rc.1","v1.4.4","v1.4.3","v1.4.2","v1.4.1","v1.4.0.rc.0","v1.4.0-rc.0","v1.4.0","v1.4","v1.3.0-rc.1","v1.3.0-rc.0","v1.3.0","v1.3","v1.2.1","v1.2.0","v1.2-rc.1","v1.2-rc.0","v1.2","v1.1.1","v1.1.0","v1.1","v1.0.0","v1.0","v1","stable","master","loadtest","latest","1.5.0-rc.0"]},{"name":"plausibleanalytics","image":"plausible/analytics","tags":["v1.5.0-rc.2","v1.5.0-rc.1","v1.4.4","v1.4.3","v1.4.2","v1.4.1","v1.4.0.rc.0","v1.4.0-rc.0","v1.4.0","v1.4","v1.3.0-rc.1","v1.3.0-rc.0","v1.3.0","v1.3","v1.2.1","v1.2.0","v1.2-rc.1","v1.2-rc.0","v1.2","v1.1.1","v1.1.0","v1.1","v1.0.0","v1.0","v1","stable","master","loadtest","latest","1.5.0-rc.0"]},{"name":"pocketbase","image":"coollabsio/pocketbase","tags":["0.8.0-arm64","0.8.0-amd64","0.8.0-aarch64","0.8.0"]},{"name":"searxng","image":"searxng/searxng","tags":["2022.12.02-ffb72dfd","2022.12.02-890d63b9","2022.12.02-4970db05","2022.12.02-317fe0a2","2022.11.30-f19837cf","2022.11.30-44d4a171","2022.11.30-0361f836","2022.11.29-a8359dd4","2022.11.29-82af2f44","2022.11.29-768659f2","2022.11.29-5b19f892","2022.11.29-3579a38a","2022.11.29-1b2f1c17","2022.11.25-5ca6868c","2022.11.25-28ae469f","2022.11.25-1314c1c5","2022.11.19-b5371b7a","2022.11.18-fe8b0472","2022.11.18-1cdadf4b","2022.11.11-e6345758","2022.11.11-3a765113","2022.11.10-117f69fa","2022.11.09-ee4475ff","2022.11.07-d3949269","2022.11.07-8f19bdaf","2022.11.06-ae54c7d5","2022.11.06-2dc5c0e1","2022.11.05-e9f42e1c","2022.11.05-d764d94a","2022.11.05-d3a7399e"]},{"name":"trilium","image":"zadam/trilium","tags":["0.57.2","0.56.1","0.55.1","0.54.2","0.53.2","0.52.4","0.52.2","0.51.2","0.50.3","0.50.1","0.49.5","0.49.3","0.48.9","0.48.7","0.48.4","0.48.2","0.47.8","0.47.6","0.47.4","0.47.2","0.46.7","0.46.5","0.45.9","0.45.7","0.45.5","0.45.3","0.45.10","0.44.8","0.44.6","0.44.4"]},{"name":"umami-postgresql","image":"ghcr.io/umami-software/umami","tags":["postgresql-v1.39.5","postgresql-v1.39.4","postgresql-v1.39.3","postgresql-v1.39.2","postgresql-v1.39.1","postgresql-v1.39.0","postgresql-v1.38.0","postgresql-v1.37.0","postgresql-v1.36.1","postgresql-v1.36.0","postgresql-v1.35.0","postgresql-v1.34.0","postgresql-v1.33.3","postgresql-latest","mysql-v1.39.5","mysql-v1.39.4","mysql-v1.39.3","mysql-v1.39.2","mysql-v1.39.1","mysql-v1.39.0","mysql-v1.38.0","mysql-v1.37.0","mysql-v1.36.1","mysql-v1.36.0","mysql-v1.35.0","mysql-v1.34.0","mysql-v1.33.3","mysql-latest"]},{"name":"umami","image":"ghcr.io/umami-software/umami","tags":["postgresql-v1.39.5","postgresql-v1.39.4","postgresql-v1.39.3","postgresql-v1.39.2","postgresql-v1.39.1","postgresql-v1.39.0","postgresql-v1.38.0","postgresql-v1.37.0","postgresql-v1.36.1","postgresql-v1.36.0","postgresql-v1.35.0","postgresql-v1.34.0","postgresql-v1.33.3","postgresql-latest","mysql-v1.39.5","mysql-v1.39.4","mysql-v1.39.3","mysql-v1.39.2","mysql-v1.39.1","mysql-v1.39.0","mysql-v1.38.0","mysql-v1.37.0","mysql-v1.36.1","mysql-v1.36.0","mysql-v1.35.0","mysql-v1.34.0","mysql-v1.33.3","mysql-latest"]},{"name":"uptimekuma","image":"louislam/uptime-kuma","tags":["1.9.2","1.9.1","1.9.0","1.8.0","1.7.3","1.7.1","1.7.0","1.6.3","1.6.2","1.6.1","1.6.0","1.5.3","1.5.2","1.5.0","1.3.1","1.2.0","1.18.5","1.18.4","1.18.3","1.18.2","1.18.1","1.18.0","1.17.1","1.17.0","1.16.1","1.16.0","1.15.1","1.15.0","1.14.1","1.14.0"]},{"name":"vaultwarden","image":"vaultwarden/server","tags":["1.26.0","1.25.2","1.25.1","1.25.0","1.24.0","1.23.1","1.23.0","1.22.2","1.22.1","1.22.0","1.21.0"]},{"name":"vscodeserver","image":"codercom/code-server","tags":["4.8.3","4.8.2","4.8.1","4.8.0","4.7.0","4.6.0","4.5.1","4.4.0","4.2.0","4.0.2","3.9.3","3.9.1","3.8.1","3.7.4","3.7.2","3.7.0","3.6.1","3.5.0","3.4.0","3.3.0","3.2.0","3.11.1","3.10.2","3.10.0","3.1.1","3.1.0","3.0.2","3.0.0"]},{"name":"weblate","image":"weblate/weblate","tags":["latest","edge-2022-12-01-0295bd44d4d9da0e0836b9152319fba173a0825e","edge-2022-11-28-f28431a1e78f88bf49ccf539fbc00afe0925542d","edge-2022-11-26-558811de16025b83de43d2747f1fe209a5b829f1","edge-2022-11-23-4a1fe25c7b70e49156e02183a8deec3b357b9030","edge-2022-11-22-9a178e7f5c2e387329592a1dd7700671f64f6682","edge-2022-11-21-eb741ebad70211ecb1babdfd23e4f43c5a59fc7b","edge-2022-11-21-4580d37f616650cf5b0851fee051651f785e8852","edge-2022-11-21-0f74d6c4d3777dbf28affd09b45c69c85ed01d84","edge-2022-11-15-cad0a043b32c1ee61611ab258db0f01c5e6d718f","edge-2022-11-10-bf41db3afbab22384e103718094738dcfdc1a270","edge-2022-11-09-9bc90ce8b873778d2f486eccd0163bb1bb65ca6e","edge-2022-11-08-36e221037ff7097f8cd2c88d779135b6c7d3f363","edge-2022-11-08-3568e3c6759a9e9b779d98cb98393526d451466a","edge-2022-11-08-261d197970ca0679514d32ff783467972e807061","edge-2022-11-05-fa5cb203d854a11cc7850868a2890168afa3e7da","edge-2022-11-05-d93ae789eef8f065240f9fb6feb3edb236a7e6f8","edge-2022-11-05-8fc2be8e9d22e5ca2da2773488da7f72c5927ec3","edge-2022-11-05-85da67e88a113bed65530f0695ad4cddec0ed05a","edge-2022-11-05-3f4d77b6f2cb16bf008a4ef587e843ccb9c0c5d0","edge-2022-11-05-226eed520a2b32c3583c6e3247109ec8950764e7","edge-2022-11-03-487f3255cb89415fbe0769fa4b7bd2a9209deca6","edge-2022-11-02-e4171e0c5657ca38341cce8ac31f5cbdf25389eb","edge-2022-11-02-6d886c40cd62eb23d21f7c0a1840b4a7a4c51ad0","edge-2022-11-01-608df4dd95a2d1f76c15cddd9e116bb4c3229168","edge-2022-11-01-54957be78eb76f602ceae50c0b01b64b20402b2a","edge-2022-10-31-c55c7302a6c82a160ee9d711893c12d67ecd3b27","edge-2022-10-26-c69cfdd83ed1fad4a4d57398552b8c70894a6586","edge-2022-10-26-410b3aff37de5bbfacbc47642ce28b2518bee506","edge-2022-10-25-e09e2c29ed3748eb0fa248453635dd27768e8dd9"]},{"name":"wordpress-only","image":"library/wordpress","tags":["php8.1-fpm-alpine","php8.1-fpm","php8.1-apache","php8.1","php8.0-fpm-alpine","php8.0-fpm","php8.0-apache","php8.0","php7.4-fpm-alpine","php7.4-fpm","php7.4-apache","php7.4","php7.3-fpm-alpine","php7.3-fpm","php7.3-apache","php7.3","php7.2-fpm-alpine","php7.2-fpm","php7.2-apache","php7.2","php7.1-fpm-alpine","php7.1-fpm","php7.1-apache","php7.1","php7.0-fpm-alpine","php7.0-fpm","php7.0-apache","php7.0","php5.6-fpm-alpine","php5.6-fpm"]},{"name":"wordpress","image":"library/wordpress","tags":["php8.1-fpm-alpine","php8.1-fpm","php8.1-apache","php8.1","php8.0-fpm-alpine","php8.0-fpm","php8.0-apache","php8.0","php7.4-fpm-alpine","php7.4-fpm","php7.4-apache","php7.4","php7.3-fpm-alpine","php7.3-fpm","php7.3-apache","php7.3","php7.2-fpm-alpine","php7.2-fpm","php7.2-apache","php7.2","php7.1-fpm-alpine","php7.1-fpm","php7.1-apache","php7.1","php7.0-fpm-alpine","php7.0-fpm","php7.0-apache","php7.0","php5.6-fpm-alpine","php5.6-fpm"]}] \ No newline at end of file diff --git a/apps/server/src/templates.json b/apps/server/src/templates.json new file mode 100644 index 000000000..256cca609 --- /dev/null +++ b/apps/server/src/templates.json @@ -0,0 +1 @@ +[{"templateVersion":"1.0.0","defaultVersion":"0.8.0","documentation":"https://pocketbase.io/docs/","type":"pocketbase","name":"Pocketbase","description":"Open Source realtime backend in 1 file","services":{"$$id":{"image":"coollabsio/pocketbase:$$core_version","volumes":["$$id-data:/app/pb_data"],"ports":["8080"]}}},{"templateVersion":"1.0.0","defaultVersion":"1.5.0-rc.0","documentation":"https://plausible.io/doc/","type":"plausibleanalytics-arm","name":"Plausible Analytics (ARM)","description":"A lightweight and open-source website analytics tool.","labels":["analytics","statistics","plausible","gdpr","no-cookie","google analytics"],"services":{"$$id":{"name":"Plausible Analytics","command":"sh -c \"sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run\"","depends_on":["$$id-postgresql","$$id-clickhouse"],"image":"plausible/analytics:$$core_version","environment":["ADMIN_USER_EMAIL=$$config_admin_user_email","ADMIN_USER_NAME=$$config_admin_user_name","ADMIN_USER_PWD=$$secret_admin_user_pwd","BASE_URL=$$config_base_url","SECRET_KEY_BASE=$$secret_secret_key_base","DISABLE_AUTH=$$config_disable_auth","DISABLE_REGISTRATION=$$config_disable_registration","DATABASE_URL=$$secret_database_url","CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url"],"ports":["8000"]},"$$id-postgresql":{"name":"PostgreSQL","image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_USER=$$config_postgres_user","POSTGRES_DB=$$config_postgres_db"]},"$$id-clickhouse":{"name":"Clickhouse","volumes":["$$id-clickhouse-data:/var/lib/clickhouse"],"image":"clickhouse/clickhouse-server:22.6-alpine","ulimits":{"nofile":{"soft":262144,"hard":262144}},"files":[{"location":"/etc/clickhouse-server/users.d/logging.xml","content":"warningtrue"},{"location":"/etc/clickhouse-server/config.d/logging.xml","content":"00"},{"location":"/docker-entrypoint-initdb.d/init.query","content":"CREATE DATABASE IF NOT EXISTS plausible;"},{"location":"/docker-entrypoint-initdb.d/init-db.sh","content":"clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query"}]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":"You must set this to the FQDN of the Plausible Analytics instance. This is used to generate the links to the Plausible Analytics instance."},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_clickhouse_database_url","name":"CLICKHOUSE_DATABASE_URL","label":"Database URL for Clickhouse","defaultValue":"http://$$id-clickhouse:8123/plausible","description":""},{"id":"$$config_admin_user_email","name":"ADMIN_USER_EMAIL","label":"Admin Email Address","defaultValue":"admin@example.com","description":"This is the admin email. Please change it."},{"id":"$$config_admin_user_name","name":"ADMIN_USER_NAME","label":"Admin User Name","defaultValue":"$$generate_username","description":"This is the admin username. Please change it."},{"id":"$$secret_admin_user_pwd","name":"ADMIN_USER_PWD","label":"Admin User Password","defaultValue":"$$generate_password","description":"This is the admin password. Please change it.","showOnConfiguration":true},{"id":"$$secret_secret_key_base","name":"SECRET_KEY_BASE","label":"Secret Key Base","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_disable_auth","name":"DISABLE_AUTH","label":"Disable Authentication","defaultValue":"false","description":""},{"id":"$$config_disable_registration","name":"DISABLE_REGISTRATION","label":"Disable Registration","defaultValue":"true","description":""},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL Username","defaultValue":"postgresql","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"plausible","description":""},{"id":"$$config_scriptName","name":"SCRIPT_NAME","label":"Custom Script Name","defaultValue":"plausible.js","description":"This is the default script name."}]},{"templateVersion":"1.0.0","defaultVersion":"1.17","documentation":"https://docs.gitea.io","type":"gitea","name":"Gitea","description":"Gitea is a community managed lightweight code hosting solution written in Go.","labels":["storage","git"],"services":{"$$id":{"name":"Gitea","documentation":"https://docs.gitea.io","image":"gitea/gitea:$$core_version","volumes":["$$id-data:/data","/etc/timezone:/etc/timezone:ro","/etc/localtime:/etc/localtime:ro"],"environment":["USER_UID=1000","USER_GID=1000","DOMAIN=$$config_domain","SSH_DOMAIN=$$config_ssh_domain","ROOT_URL=$$config_root_url","SECRET_KEY=$$secret_secret_key","INTERNAL_TOKEN=$$secret_internal_token","SSH_PORT=22","START_SSH_SERVER=$$config_start_ssh_server"],"ports":["3000","22"],"proxy":[{"port":"22","hostPort":"$$config_hostport_ssh"}]}},"variables":[{"id":"$$config_hostport_ssh","name":"SSH_PORT","label":"SSH Port","defaultValue":"8022","description":"","required":true},{"id":"$$config_domain","name":"DOMAIN","label":"Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_ssh_domain","name":"SSH_DOMAIN","label":"SSH Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_start_ssh_server","name":"START_SSH_SERVER","label":"Start SSH Server","defaultValue":"true","description":""},{"id":"$$config_root_url","name":"ROOT_URL","label":"Root URL of Gitea","defaultValue":"$$generate_fqdn_slash","description":""},{"id":"$$secret_secret_key","name":"SECRET_KEY","label":"Secret Key","defaultValue":"$$generate_hex(32)","description":""},{"id":"$$secret_internal_token","name":"INTERNAL_TOKEN","label":"Internal JWT Token","defaultValue":"$$generate_token","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"20.0","documentation":"https://www.keycloak.org/documentation","type":"keycloak","name":"Keycloak","description":"Keycloak provides user federation, strong authentication, user management, fine-grained authorization, and more.","labels":["authentication","authorization","oidconnect","saml2"],"services":{"$$id":{"name":"Keycloak","command":"start --db=postgres --features=token-exchange --import-realm","depends_on":["$$id-postgresql"],"image":"quay.io/keycloak/keycloak:$$core_version","volumes":["$$id-import:/opt/keycloak/data/import"],"environment":["KC_HEALTH_ENABLED=true","KC_PROXY=edge","KC_DB=postgres","KC_HOSTNAME=$$config_keycloak_domain","KEYCLOAK_ADMIN=$$config_admin_user","KEYCLOAK_ADMIN_PASSWORD=$$secret_keycloak_admin_password","KC_DB_PASSWORD=$$secret_postgres_password","KC_DB_USERNAME=$$config_postgres_user","KC_DB_URL=$$secret_keycloak_database_url"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]}},"variables":[{"id":"$$config_keycloak_domain","name":"KEYCLOAK_DOMAIN","label":"Keycloak Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$secret_keycloak_database_url","name":"KEYCLOAK_DATABASE_URL","label":"Keycloak Database Url","defaultValue":"jdbc:postgresql://$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$config_admin_user","name":"KEYCLOAK_ADMIN","label":"Keycloak Admin User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_keycloak_admin_password","name":"KEYCLOAK_ADMIN_PASSWORD","label":"Keycloak Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"keycloak","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v3.6","documentation":"https://github.com/freyacodes/Lavalink","description":"Standalone audio sending node based on Lavaplayer.","type":"lavalink","name":"Lavalink","labels":["discord","discord bot","audio","lavalink","jda"],"services":{"$$id":{"name":"Lavalink","image":"fredboat/lavalink:$$core_version","environment":[],"volumes":["$$id-lavalink:/lavalink"],"ports":["2333"],"files":[{"location":"/opt/Lavalink/application.yml","content":"server:\n port: $$config_port\n address: 0.0.0.0\nlavalink:\n server:\n password: \"$$secret_password\"\n sources:\n youtube: true\n bandcamp: true\n soundcloud: true\n twitch: true\n vimeo: true\n http: true\n local: false\n\nlogging:\n file:\n path: ./logs/\n\n level:\n root: INFO\n lavalink: INFO\n\n logback:\n rollingpolicy:\n max-file-size: 1GB\n max-history: 30"}]}},"variables":[{"id":"$$config_port","name":"PORT","label":"Port","defaultValue":"2333","required":true},{"id":"$$secret_password","name":"PASSWORD","label":"Password","defaultValue":"$$generate_password","required":true}]},{"templateVersion":"1.0.0","defaultVersion":"v1.8.6","documentation":"https://docs.appsmith.com/getting-started/setup/instance-configuration/","type":"appsmith","name":"Appsmith","description":"Fastest way to build internal apps over any database or API.","services":{"$$id":{"image":"appsmith/appsmith-ce:$$core_version","environment":["APPSMITH_MAIL_ENABLED=$$config_appsmith_mail_enabled","APPSMITH_DISABLE_TELEMETRY=$$config_appsmith_disable_telemetry","APPSMITH_DISABLE_INTERCOM=$$config_appsmith_disable_intercom"],"volumes":["$$id-stacks-data:/appsmith-stacks"],"ports":["80"]}},"variables":[{"id":"$$config_appsmith_mail_enabled","name":"APPSMITH_MAIL_ENABLED","label":"Enable Mail","defaultValue":"false","description":""},{"id":"$$config_appsmith_disable_telemetry","name":"APPSMITH_DISABLE_TELEMETRY","label":"Disable Telemetry","defaultValue":"true","description":""},{"id":"$$config_appsmith_disable_intercom","name":"APPSMITH_DISABLE_INTERCOM","label":"Disable Intercom","defaultValue":"true","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"0.56.2","documentation":"https://hub.docker.com/r/zadam/trilium","description":"A hierarchical note taking application with focus on building large personal knowledge bases.","labels":["personal","knowledge","notes","wiki"],"type":"trilium","name":"Trilium Notes","services":{"$$id":{"image":"zadam/trilium:$$core_version","environment":[],"volumes":["$$id-trilium:/home/node/trilium-data"],"ports":["8080"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.9.2","documentation":"https://hub.docker.com/r/louislam/uptime-kuma","description":"A free & fancy self-hosted monitoring tool.","labels":["uptime"],"type":"uptimekuma","name":"UptimeKuma","services":{"$$id":{"image":"louislam/uptime-kuma:$$core_version","environment":[],"volumes":["$$id-uptimekuma:/app/data"],"ports":["3001"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"5.8","documentation":"https://hub.docker.com/r/silviof/docker-languagetool","description":"A multilingual grammar, style and spell checker.","type":"languagetool","name":"LanguageTool","services":{"$$id":{"image":"silviof/docker-languagetool:$$core_version","environment":[],"volumes":["$$id-ngrams:/ngrams"],"ports":["8010"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.26.0","documentation":"https://hub.docker.com/r/vaultwarden/server","description":"Bitwarden compatible server written in Rust.","type":"vaultwarden","name":"VaultWarden","labels":["bitwarden","password manager"],"services":{"$$id":{"image":"vaultwarden/server:$$core_version","environment":[],"volumes":["$$id-data:/data"],"ports":["80"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"9.2.3","documentation":"https://hub.docker.com/r/grafana/grafana","type":"grafana","name":"Grafana","description":"Grafana allows you to query, visualize, alert on and understand your metrics.","labels":["monitoring","metrics","dashboard"],"services":{"$$id":{"image":"grafana/grafana:$$core_version","environment":[],"volumes":["$$id-config:/etc/grafana","$$id-grafana:/var/lib/grafana"],"ports":["3000"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.0.3","documentation":"https://appwrite.io/docs","type":"appwrite","name":"Appwrite","description":"Secure Backend Server for Web, Mobile & Flutter Developers.","labels":["serverless","backend","storage","api"],"services":{"$$id":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_WORKER_PER_CORE=$$config__app_worker_per_core","_APP_LOCALE=$$config__app_locale","_APP_CONSOLE_WHITELIST_ROOT=$$config__app_console_whitelist_root","_APP_CONSOLE_WHITELIST_EMAILS=$$config__app_console_whitelist_emails","_APP_CONSOLE_WHITELIST_IPS=$$config__app_console_whitelist_ips","_APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name","_APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_SYSTEM_RESPONSE_FORMAT=$$config__app_system_response_format","_APP_OPTIONS_ABUSE=$$config__app_options_abuse","_APP_OPTIONS_FORCE_HTTPS=$$config__app_options_force_https","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_SMTP_HOST=$$config__app_smtp_host","_APP_SMTP_PORT=$$config__app_smtp_port","_APP_SMTP_SECURE=$$config__app_smtp_secure","_APP_SMTP_USERNAME=$$config__app_smtp_username","_APP_SMTP_PASSWORD=$$secret__app_smtp_password","_APP_USAGE_STATS=$$config__app_usage_stats","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_STORAGE_LIMIT=$$config__app_storage_limit","_APP_STORAGE_PREVIEW_LIMIT=$$config__app_storage_preview_limit","_APP_STORAGE_ANTIVIRUS=$$config__app_storage_antivirus_enabled","_APP_STORAGE_ANTIVIRUS_HOST=$$config__app_storage_antivirus_host","_APP_STORAGE_ANTIVIRUS_PORT=$$config__app_storage_antivirus_port","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","_APP_FUNCTIONS_SIZE_LIMIT=$$config__app_functions_size_limit","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout","_APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers","_APP_FUNCTIONS_CPUS=$$config__app_functions_cpus","_APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated","_APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap","_APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_STATSD_HOST=$$config__app_statsd_host","_APP_STATSD_PORT=$$config__app_statsd_port","_APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval","_APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution","_APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache","_APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse","_APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit","_APP_SMS_PROVIDER=$$config__app_sms_provider","_APP_SMS_FROM=$$config__app_sms_from","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-uploads:/storage/uploads","$$id-cache:/storage/cache","$$id-config:/storage/config","$$id-certificates:/storage/certificates","$$id-functions:/storage/functions"],"ports":["80"],"proxy":[{"port":"80"}]},"$$id-executor":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout","_APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers","_APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes","_APP_FUNCTIONS_CPUS=$$config__app_functions_cpus","_APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated","_APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap","_APP_FUNCTIONS_INACTIVE_THRESHOLD=$$config__app_functions_inactive_threshold","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username","DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-functions:/storage/functions","$$id-builds:/storage/builds","/var/run/docker.sock:/var/run/docker.sock"],"entrypoint":"executor"},"$$id-influxdb":{"image":"appwrite/influxdb:1.5.0","environment":[],"volumes":["$$id-influxdb:/var/lib/influxdb"]},"$$id-maintenance":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval","_APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution","_APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache","_APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse","_APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"maintenance"},"$$id-mariadb":{"image":"mariadb:10.7","command":"--innodb-flush-method fsync","environment":["MARIADB_ROOT_PASSWORD=$$secret__app_db_root_pass","MARIADB_DATABASE=$$config__app_db_schema","MARIADB_USER=$$config__app_db_user","MARIADB_PASSWORD=$$secret__app_db_pass","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-mariadb:/var/lib/mysql"]},"$$id-realtime":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_WORKER_PER_CORE=$$config__app_worker_per_core","_APP_OPTIONS_ABUSE=$$config__app_options_abuse","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_USAGE_STATS=$$config__app_usage_stats","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"realtime","proxy":[{"port":"80","pathPrefix":"/v1/realtime"}]},"$$id-redis":{"image":"redis:7.0.4-alpine","command":"--maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples 5","environment":[],"volumes":["$$id-redis:/data"]},"$$id-schedule":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"schedule"},"$$id-telegraf":{"image":"appwrite/telegraf:1.4.0","environment":["_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-influxdb:/var/lib/influxdb"]},"$$id-usage-database":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval","_APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"usage --type database"},"$$id-usage":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval","_APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"usage --type timeseries"},"$$id-worker-audits":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-audits"},"$$id-worker-builds":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-builds"},"$$id-worker-certificates":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-config:/storage/config","$$id-certificates:/storage/certificates"],"entrypoint":"worker-certificates"},"$$id-worker-databases":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-databases"},"$$id-worker-deletes":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-uploads:/storage/uploads","$$id-cache:/storage/cache","$$id-functions:/storage/functions","$$id-builds:/storage/builds","$$id-certificates:/storage/certificates"],"entrypoint":"worker-deletes"},"$$id-worker-functions":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_USAGE_STATS=$$config__app_usage_stats","DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username","DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-functions"},"$$id-worker-mails":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name","_APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_SMTP_HOST=$$config__app_smtp_host","_APP_SMTP_PORT=$$config__app_smtp_port","_APP_SMTP_SECURE=$$config__app_smtp_secure","_APP_SMTP_USERNAME=$$config__app_smtp_username","_APP_SMTP_PASSWORD=$$secret__app_smtp_password","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-mails"},"$$id-worker-messaging":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_SMS_PROVIDER=$$config__app_sms_provider","_APP_SMS_FROM=$$config__app_sms_from","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-messaging"},"$$id-worker-webhooks":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-webhooks"}},"variables":[{"id":"$$config__app_influxdb_host","name":"_APP_INFLUXDB_HOST","label":"InfluxDB | _APP_INFLUXDB_HOST","defaultValue":"$$id-influxdb","description":""},{"id":"$$config__app_influxdb_port","name":"_APP_INFLUXDB_PORT","label":"InfluxDB | _APP_INFLUXDB_PORT","defaultValue":"8086","description":"InfluxDB server TCP port."},{"id":"$$config__app_env","name":"_APP_ENV","label":"General | _APP_ENV","defaultValue":"production","description":"Set your server running environment."},{"id":"$$config__app_worker_per_core","name":"_APP_WORKER_PER_CORE","label":"General | _APP_WORKER_PER_CORE","defaultValue":"6","description":"Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance."},{"id":"$$config__app_locale","name":"_APP_LOCALE","label":"General | _APP_LOCALE","defaultValue":"en","description":"Set your Appwrite's locale. By default, the locale is set to 'en'."},{"id":"$$config__app_console_whitelist_root","name":"_APP_CONSOLE_WHITELIST_ROOT","label":"General | _APP_CONSOLE_WHITELIST_ROOT","defaultValue":"enabled","description":"This option allows you to disable the creation of new users on the Appwrite console. When enabled only 1 user will be able to use the registration form. New users can be added by inviting them to your project. By default this option is enabled."},{"id":"$$config__app_console_whitelist_emails","name":"_APP_CONSOLE_WHITELIST_EMAILS","label":"General | _APP_CONSOLE_WHITELIST_EMAILS","defaultValue":"","description":"This option allows you to limit creation of new users on the Appwrite console. This option is very useful for small teams or sole developers. To enable it, pass a list of allowed email addresses separated by a comma."},{"id":"$$config__app_console_whitelist_ips","name":"_APP_CONSOLE_WHITELIST_IPS","label":"General | _APP_CONSOLE_WHITELIST_IPS","defaultValue":"","description":"This last option allows you to limit creation of users in Appwrite console for users sharing the same set of IP addresses. This option is very useful for team working with a VPN service or a company IP.\\n\\nTo enable/activate this option, pass a list of allowed IP addresses separated by a comma."},{"id":"$$config__app_system_email_name","name":"_APP_SYSTEM_EMAIL_NAME","label":"General | _APP_SYSTEM_EMAIL_NAME","defaultValue":"Appwrite","description":"This is the sender name value that will appear on email messages sent to developers from the Appwrite console. You can use url encoded strings for spaces and special chars."},{"id":"$$config__app_system_email_address","name":"_APP_SYSTEM_EMAIL_ADDRESS","label":"General | _APP_SYSTEM_EMAIL_ADDRESS","defaultValue":"team@appwrite.io","description":"This is the sender email address that will appear on email messages sent to developers from the Appwrite console. You should choose an email address that is allowed to be used from your SMTP server to avoid the server email ending in the users' SPAM folders."},{"id":"$$config__app_system_security_email_address","name":"_APP_SYSTEM_SECURITY_EMAIL_ADDRESS","label":"General | _APP_SYSTEM_SECURITY_EMAIL_ADDRESS","defaultValue":"certs@appwrite.io","description":"This is the email address used to issue SSL certificates for custom domains or the user agent in your webhooks payload."},{"id":"$$config__app_system_response_format","name":"_APP_SYSTEM_RESPONSE_FORMAT","label":"General | _APP_SYSTEM_RESPONSE_FORMAT","defaultValue":"","description":"Use this environment variable to set the default Appwrite HTTP response format to support an older version of Appwrite. This option is useful to overcome breaking changes between versions. You can also use the X-Appwrite-Response-Format HTTP request header to overwrite the response for a specific request. This variable accepts any valid Appwrite version. To use the current version format, leave the value of the variable empty."},{"id":"$$config__app_options_abuse","name":"_APP_OPTIONS_ABUSE","label":"General | _APP_OPTIONS_ABUSE","defaultValue":"enabled","description":"Allows you to disable abuse checks and API rate limiting. By default, set to 'enabled'. To cancel the abuse checking, set to 'disabled'. It is not recommended to disable this check-in a production environment."},{"id":"$$config__app_options_force_https","name":"_APP_OPTIONS_FORCE_HTTPS","label":"General | _APP_OPTIONS_FORCE_HTTPS","defaultValue":"disabled","description":"Allows you to force HTTPS connection to your API. This feature redirects any HTTP call to HTTPS and adds the 'Strict-Transport-Security' header to all HTTP responses."},{"id":"$$secret__app_openssl_key_v1","name":"_APP_OPENSSL_KEY_V1","label":"General | _APP_OPENSSL_KEY_V1","defaultValue":"$$generate_hex(256)","description":"This is your server private secret key that is used to encrypt all sensitive data on your server. Appwrite server encrypts all secret data on your server like webhooks, HTTP passwords, user sessions, and storage files. Keep it a secret and have a backup for it."},{"id":"$$config__app_domain","name":"_APP_DOMAIN","label":"General | _APP_DOMAIN","defaultValue":"$$generate_domain","description":"Your Appwrite domain address. When setting a public suffix domain, Appwrite will attempt to issue a valid SSL certificate automatically. When used with a dev domain, Appwrite will assign a self-signed SSL certificate. The default value is 'localhost'."},{"id":"$$config__app_domain_target","name":"_APP_DOMAIN_TARGET","label":"General | _APP_DOMAIN_TARGET","defaultValue":"$$generate_fqdn","description":"A DNS A record hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite '_APP_DOMAIN' variable. The default value is 'localhost'."},{"id":"$$config__app_redis_host","name":"_APP_REDIS_HOST","label":"Redis | _APP_REDIS_HOST","defaultValue":"$$id-redis","description":""},{"id":"$$config__app_redis_port","name":"_APP_REDIS_PORT","label":"Redis | _APP_REDIS_PORT","defaultValue":"6379","description":"Redis server TCP port."},{"id":"$$config__app_redis_user","name":"_APP_REDIS_USER","label":"Redis | _APP_REDIS_USER","defaultValue":"","description":"Redis server user. This is an optional variable. Default value is an empty string."},{"id":"$$secret__app_redis_pass","name":"_APP_REDIS_PASS","label":"Redis | _APP_REDIS_PASS","defaultValue":"","description":"Redis server password. This is an optional variable. Default value is an empty string."},{"id":"$$config__app_db_host","name":"_APP_DB_HOST","label":"MariaDB | _APP_DB_HOST","defaultValue":"$$id-mariadb","description":""},{"id":"$$config__app_db_port","name":"_APP_DB_PORT","label":"MariaDB | _APP_DB_PORT","defaultValue":"3306","description":"MariaDB server TCP port."},{"id":"$$config__app_db_schema","name":"_APP_DB_SCHEMA","label":"MariaDB | _APP_DB_SCHEMA","defaultValue":"appwrite","description":"MariaDB server database schema."},{"id":"$$config__app_db_user","name":"_APP_DB_USER","label":"MariaDB | _APP_DB_USER","defaultValue":"user","description":"MariaDB server user name."},{"id":"$$secret__app_db_root_pass","name":"MARIADB_ROOT_PASSWORD","label":"MariaDB | MARIADB_ROOT_PASSWORD","defaultValue":"$$generate_hex(16)","description":"MariaDB server root user password."},{"id":"$$secret__app_db_pass","name":"_APP_DB_PASS","label":"MariaDB | _APP_DB_PASS","defaultValue":"$$generate_hex(16)","description":"MariaDB server user password."},{"id":"$$config__app_smtp_host","name":"_APP_SMTP_HOST","label":"SMTP | _APP_SMTP_HOST","defaultValue":"","description":"SMTP server host name address. Use an empty string to disable all mail sending from the server. The default value for this variable is an empty string."},{"id":"$$config__app_smtp_port","name":"_APP_SMTP_PORT","label":"SMTP | _APP_SMTP_PORT","defaultValue":"","description":"SMTP server TCP port. Empty by default."},{"id":"$$config__app_smtp_secure","name":"_APP_SMTP_SECURE","label":"SMTP | _APP_SMTP_SECURE","defaultValue":"","description":"SMTP secure connection protocol. Empty by default, change to 'tls' if running on a secure connection."},{"id":"$$config__app_smtp_username","name":"_APP_SMTP_USERNAME","label":"SMTP | _APP_SMTP_USERNAME","defaultValue":"","description":"SMTP server user name. Empty by default."},{"id":"$$secret__app_smtp_password","name":"_APP_SMTP_PASSWORD","label":"SMTP | _APP_SMTP_PASSWORD","defaultValue":"","description":"SMTP server user password. Empty by default."},{"id":"$$config__app_usage_stats","name":"_APP_USAGE_STATS","label":"General | _APP_USAGE_STATS","defaultValue":"enabled","description":"This variable allows you to disable the collection and displaying of usage stats. This value is set to 'enabled' by default, to disable the usage stats set the value to 'disabled'. When disabled, it's recommended to turn off the Worker Usage, Influxdb and Telegraf containers for better resource usage."},{"id":"$$config__app_storage_limit","name":"_APP_STORAGE_LIMIT","label":"Storage | _APP_STORAGE_LIMIT","defaultValue":"30000000","description":"Maximum file size allowed for file upload. The default value is 30MB. You should pass your size limit value in bytes."},{"id":"$$config__app_storage_preview_limit","name":"_APP_STORAGE_PREVIEW_LIMIT","label":"Storage | _APP_STORAGE_PREVIEW_LIMIT","defaultValue":"20000000","description":"Maximum file size allowed for file image preview. The default value is 20MB. You should pass your size limit value in bytes."},{"id":"$$config__app_storage_antivirus_enabled","name":"_APP_STORAGE_ANTIVIRUS","label":"Storage | _APP_STORAGE_ANTIVIRUS","defaultValue":"disabled","description":"This variable allows you to disable the internal anti-virus scans. This value is set to 'disabled' by default, to enable the scans set the value to 'enabled'. Before enabling, you must add the ClamAV service and depend on it on main Appwrite service."},{"id":"$$config__app_storage_antivirus_host","name":"_APP_STORAGE_ANTIVIRUS_HOST","label":"Storage | _APP_STORAGE_ANTIVIRUS_HOST","defaultValue":"clamav","description":"ClamAV server host name address."},{"id":"$$config__app_storage_antivirus_port","name":"_APP_STORAGE_ANTIVIRUS_PORT","label":"Storage | _APP_STORAGE_ANTIVIRUS_PORT","defaultValue":"3310","description":"ClamAV server TCP port."},{"id":"$$config__app_storage_device","name":"_APP_STORAGE_DEVICE","label":"Storage | _APP_STORAGE_DEVICE","defaultValue":"Local","description":"Select default storage device. The default value is 'Local'. List of supported adapters are 'Local', 'S3', 'DOSpaces', 'Backblaze', 'Linode' and 'Wasabi'."},{"id":"$$secret__app_storage_s3_access_key","name":"_APP_STORAGE_S3_ACCESS_KEY","label":"Storage | _APP_STORAGE_S3_ACCESS_KEY","defaultValue":"","description":"AWS S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your AWS console."},{"id":"$$secret__app_storage_s3_secret","name":"_APP_STORAGE_S3_SECRET","label":"Storage | _APP_STORAGE_S3_SECRET","defaultValue":"","description":"AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console."},{"id":"$$config__app_storage_s3_region","name":"_APP_STORAGE_S3_REGION","label":"Storage | _APP_STORAGE_S3_REGION","defaultValue":"us-east-1","description":"AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console."},{"id":"$$config__app_storage_s3_bucket","name":"_APP_STORAGE_S3_BUCKET","label":"Storage | _APP_STORAGE_S3_BUCKET","defaultValue":"","description":"AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console."},{"id":"$$secret__app_storage_do_spaces_access_key","name":"_APP_STORAGE_DO_SPACES_ACCESS_KEY","label":"Storage | _APP_STORAGE_DO_SPACES_ACCESS_KEY","defaultValue":"","description":"DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console."},{"id":"$$secret__app_storage_do_spaces_secret","name":"_APP_STORAGE_DO_SPACES_SECRET","label":"Storage | _APP_STORAGE_DO_SPACES_SECRET","defaultValue":"","description":"DigitalOcean spaces secret key. Required when the storage adapter is set to DOSpaces. You can get your secret key from your DigitalOcean console."},{"id":"$$config__app_storage_do_spaces_region","name":"_APP_STORAGE_DO_SPACES_REGION","label":"Storage | _APP_STORAGE_DO_SPACES_REGION","defaultValue":"us-east-1","description":"DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console."},{"id":"$$config__app_storage_do_spaces_bucket","name":"_APP_STORAGE_DO_SPACES_BUCKET","label":"Storage | _APP_STORAGE_DO_SPACES_BUCKET","defaultValue":"","description":"DigitalOcean spaces bucket. Required when storage adapter is set to DOSpaces. You can create spaces in your DigitalOcean console."},{"id":"$$secret__app_storage_backblaze_access_key","name":"_APP_STORAGE_BACKBLAZE_ACCESS_KEY","label":"Storage | _APP_STORAGE_BACKBLAZE_ACCESS_KEY","defaultValue":"","description":"Backblaze access key. Required when the storage adapter is set to Backblaze. Your Backblaze keyID will be your access key. You can get your keyID from your Backblaze console."},{"id":"$$secret__app_storage_backblaze_secret","name":"_APP_STORAGE_BACKBLAZE_SECRET","label":"Storage | _APP_STORAGE_BACKBLAZE_SECRET","defaultValue":"","description":"Backblaze secret key. Required when the storage adapter is set to Backblaze. Your Backblaze applicationKey will be your secret key. You can get your applicationKey from your Backblaze console."},{"id":"$$config__app_storage_backblaze_region","name":"_APP_STORAGE_BACKBLAZE_REGION","label":"Storage | _APP_STORAGE_BACKBLAZE_REGION","defaultValue":"us-west-004","description":"Backblaze region. Required when storage adapter is set to Backblaze. You can find your region info from your Backblaze console."},{"id":"$$config__app_storage_backblaze_bucket","name":"_APP_STORAGE_BACKBLAZE_BUCKET","label":"Storage | _APP_STORAGE_BACKBLAZE_BUCKET","defaultValue":"","description":"Backblaze bucket. Required when storage adapter is set to Backblaze. You can create your bucket from your Backblaze console."},{"id":"$$secret__app_storage_linode_access_key","name":"_APP_STORAGE_LINODE_ACCESS_KEY","label":"Storage | _APP_STORAGE_LINODE_ACCESS_KEY","defaultValue":"","description":"Linode object storage access key. Required when the storage adapter is set to Linode. You can get your access key from your Linode console."},{"id":"$$secret__app_storage_linode_secret","name":"_APP_STORAGE_LINODE_SECRET","label":"Storage | _APP_STORAGE_LINODE_SECRET","defaultValue":"","description":"Linode object storage secret key. Required when the storage adapter is set to Linode. You can get your secret key from your Linode console."},{"id":"$$config__app_storage_linode_region","name":"_APP_STORAGE_LINODE_REGION","label":"Storage | _APP_STORAGE_LINODE_REGION","defaultValue":"eu-central-1","description":"Linode object storage region. Required when storage adapter is set to Linode. You can find your region info from your Linode console."},{"id":"$$config__app_storage_linode_bucket","name":"_APP_STORAGE_LINODE_BUCKET","label":"Storage | _APP_STORAGE_LINODE_BUCKET","defaultValue":"","description":"Linode object storage bucket. Required when storage adapter is set to Linode. You can create buckets in your Linode console."},{"id":"$$secret__app_storage_wasabi_access_key","name":"_APP_STORAGE_WASABI_ACCESS_KEY","label":"Storage | _APP_STORAGE_WASABI_ACCESS_KEY","defaultValue":"","description":"Wasabi access key. Required when the storage adapter is set to Wasabi. You can get your access key from your Wasabi console."},{"id":"$$secret__app_storage_wasabi_secret","name":"_APP_STORAGE_WASABI_SECRET","label":"Storage | _APP_STORAGE_WASABI_SECRET","defaultValue":"","description":"Wasabi secret key. Required when the storage adapter is set to Wasabi. You can get your secret key from your Wasabi console."},{"id":"$$config__app_storage_wasabi_region","name":"_APP_STORAGE_WASABI_REGION","label":"Storage | _APP_STORAGE_WASABI_REGION","defaultValue":"eu-central-1","description":"Wasabi region. Required when storage adapter is set to Wasabi. You can find your region info from your Wasabi console."},{"id":"$$config__app_storage_wasabi_bucket","name":"_APP_STORAGE_WASABI_BUCKET","label":"Storage | _APP_STORAGE_WASABI_BUCKET","defaultValue":"","description":"Wasabi bucket. Required when storage adapter is set to Wasabi. You can create buckets in your Wasabi console."},{"id":"$$config__app_functions_size_limit","name":"_APP_FUNCTIONS_SIZE_LIMIT","label":"Functions | _APP_FUNCTIONS_SIZE_LIMIT","defaultValue":"30000000","description":"The maximum size deployment in bytes. The default value is 30MB."},{"id":"$$config__app_functions_timeout","name":"_APP_FUNCTIONS_TIMEOUT","label":"Functions | _APP_FUNCTIONS_TIMEOUT","defaultValue":"900","description":"The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds."},{"id":"$$config__app_functions_build_timeout","name":"_APP_FUNCTIONS_BUILD_TIMEOUT","label":"Functions | _APP_FUNCTIONS_BUILD_TIMEOUT","defaultValue":"900","description":"The maximum number of seconds allowed as a timeout value when building a new function. The default value is 900 seconds."},{"id":"$$config__app_functions_containers","name":"_APP_FUNCTIONS_CONTAINERS","label":"Functions | _APP_FUNCTIONS_CONTAINERS","defaultValue":"10","description":"The maximum number of containers Appwrite is allowed to keep alive in the background for function environments. Running containers allow faster execution time as there is no need to recreate each container every time a function gets executed. The default value is 10."},{"id":"$$config__app_functions_cpus","name":"_APP_FUNCTIONS_CPUS","label":"Functions | _APP_FUNCTIONS_CPUS","defaultValue":"","description":"The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it's empty, CPU limit will be disabled."},{"id":"$$config__app_functions_memory_allocated","name":"_APP_FUNCTIONS_MEMORY","label":"Functions | _APP_FUNCTIONS_MEMORY","defaultValue":"","description":"The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it's empty, memory limit will be disabled."},{"id":"$$config__app_functions_memory_swap","name":"_APP_FUNCTIONS_MEMORY_SWAP","label":"Functions | _APP_FUNCTIONS_MEMORY_SWAP","defaultValue":"","description":"The maximum amount of swap memory a single cloud function is allowed to use in megabytes. The default value is empty. When it's empty, swap memory limit will be disabled."},{"id":"$$config__app_functions_runtimes","name":"_APP_FUNCTIONS_RUNTIMES","label":"Functions | _APP_FUNCTIONS_RUNTIMES","defaultValue":"node-18.0","description":"This option allows you to limit the available environments for cloud functions. This option is very useful for low-cost servers to safe disk space.\nTo enable/activate this option, pass a list of allowed environments separated by a comma.\nCurrently, supported environments are: node-14.5, node-16.0, node-18.0, php-8.0, php-8.1, ruby-3.0, ruby-3.1, python-3.8, python-3.9, python-3.10, deno-1.21, deno-1.24, dart-2.15, dart-2.16, dart-2.17, dotnet-3.1, dotnet-6.0, java-8.0, java-11.0, java-17.0, java-18.0, swift-5.5, kotlin-1.6, cpp-17.0"},{"id":"$$secret__app_executor_secret","name":"_APP_EXECUTOR_SECRET","label":"Functions | _APP_EXECUTOR_SECRET","defaultValue":"$$generate_hex(16)","description":"The secret key used by Appwrite to communicate with the function executor."},{"id":"$$config__app_executor_host","name":"_APP_EXECUTOR_HOST","label":"","defaultValue":"http://$$id-executor/v1","description":""},{"id":"$$config__app_logging_provider","name":"_APP_LOGGING_PROVIDER","label":"General | _APP_LOGGING_PROVIDER","defaultValue":"","description":"This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, to enable the logger set the value to one of 'sentry', 'raygun', 'appsignal', 'logowl'"},{"id":"$$config__app_logging_config","name":"_APP_LOGGING_CONFIG","label":"General | _APP_LOGGING_CONFIG","defaultValue":"","description":"This variable configures authentication to 3rd party error logging providers. If using Sentry, this should be 'SENTRY_API_KEY;SENTRY_APP_ID'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket."},{"id":"$$config__app_statsd_host","name":"_APP_STATSD_HOST","label":"","defaultValue":"$$id-telegraf","description":""},{"id":"$$config__app_statsd_port","name":"_APP_STATSD_PORT","label":"StatsD | _APP_STATSD_PORT","defaultValue":"8125","description":"StatsD server TCP port."},{"id":"$$config__app_maintenance_interval","name":"_APP_MAINTENANCE_INTERVAL","label":"Functions | _APP_MAINTENANCE_INTERVAL","defaultValue":"86400","description":"Interval value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 86400 seconds (1 day)."},{"id":"$$config__app_maintenance_retention_execution","name":"_APP_MAINTENANCE_RETENTION_EXECUTION","label":"Functions | _APP_MAINTENANCE_RETENTION_EXECUTION","defaultValue":"1209600","description":"The maximum duration (in seconds) upto which to retain execution logs. The default value is 1209600 seconds (14 days)."},{"id":"$$config__app_maintenance_retention_cache","name":"_APP_MAINTENANCE_RETENTION_CACHE","label":"Functions | _APP_MAINTENANCE_RETENTION_CACHE","defaultValue":"2592000","description":"The maximum duration (in seconds) upto which to retain cached files. The default value is 2592000 seconds (30 days)."},{"id":"$$config__app_maintenance_retention_abuse","name":"_APP_MAINTENANCE_RETENTION_ABUSE","label":"Functions | _APP_MAINTENANCE_RETENTION_ABUSE","defaultValue":"86400","description":"The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day)."},{"id":"$$config__app_maintenance_retention_audit","name":"_APP_MAINTENANCE_RETENTION_AUDIT","label":"Functions | _APP_MAINTENANCE_RETENTION_AUDIT","defaultValue":"1209600","description":"The maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days)."},{"id":"$$config__app_sms_provider","name":"_APP_SMS_PROVIDER","label":"Phone | _APP_SMS_PROVIDER","defaultValue":"","description":"Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'. Available providers are twilio, text-magic, telesign, msg91, and vonage."},{"id":"$$config__app_sms_from","name":"_APP_SMS_FROM","label":"Phone | _APP_SMS_FROM","defaultValue":"","description":"Phone number used for sending out messages. Must start with a leading '+' and maximum of 15 digits without spaces (+123456789)."},{"id":"$$config__app_functions_inactive_threshold","name":"_APP_FUNCTIONS_INACTIVE_THRESHOLD","label":"Functions | _APP_FUNCTIONS_INACTIVE_THRESHOLD","defaultValue":"60","description":"The minimum time a function can be inactive before it's container is shutdown and put to sleep. The default value is 60 seconds"},{"id":"$$config_open_runtimes_network","name":"OPEN_RUNTIMES_NETWORK","label":"","defaultValue":"$$generate_network","description":""},{"id":"$$config_dockerhub_pull_username","name":"DOCKERHUB_PULL_USERNAME","label":"Functions | DOCKERHUB_PULL_USERNAME","defaultValue":"","description":"The username for hub.docker.com. This variable is used to pull images from hub.docker.com."},{"id":"$$secret_dockerhub_pull_password","name":"DOCKERHUB_PULL_PASSWORD","label":"Functions | DOCKERHUB_PULL_PASSWORD","defaultValue":"","description":"The password for hub.docker.com. This variable is used to pull images from hub.docker.com."},{"id":"$$config__app_usage_timeseries_interval","name":"_APP_USAGE_TIMESERIES_INTERVAL","label":"General | _APP_USAGE_TIMESERIES_INTERVAL","defaultValue":"30","description":"Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats and syncing it to mariadb from InfluxDB. The default value is 30 seconds."},{"id":"$$config__app_usage_database_interval","name":"_APP_USAGE_DATABASE_INTERVAL","label":"General | _APP_USAGE_DATABASE_INTERVAL","defaultValue":"900","description":"Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats from data in Appwrite Database. The default value is 15 minutes."}]},{"templateVersion":"1.0.0","defaultVersion":"latest","documentation":"https://docs.weblate.org/en/latest/admin/install/docker.html","description":"A copylefted libre software web-based continuous localization system.","type":"weblate","name":"Weblate","labels":["translate","localization"],"services":{"$$id":{"name":"Weblate","depends_on":["$$id-postgresql","$$id-redis"],"image":"weblate/weblate:$$core_version","volumes":["$$id-data:/app/data"],"environment":["WEBLATE_SITE_DOMAIN=$$config_weblate_site_domain","WEBLATE_ADMIN_PASSWORD=$$secret_weblate_admin_password","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_USER=$$config_postgres_user","POSTGRES_DATABASE=$$config_postgres_db","POSTGRES_HOST=$$id-postgresql","POSTGRES_PORT=5432","REDIS_HOST=$$id-redis"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]},"$$id-redis":{"name":"Redis","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-redis-data:/data"],"environment":[],"ports":[]}},"variables":[{"id":"$$config_weblate_site_domain","name":"WEBLATE_SITE_DOMAIN","label":"Weblate Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$secret_weblate_admin_password","name":"WEBLATE_ADMIN_PASSWORD","label":"Weblate Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"weblate","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"2022.10.14-1a5b0965","documentation":"https://docs.searxng.org/","type":"searxng","name":"SearXNG","description":"Free internet metasearch engine which aggregates results from more than 70 search services.","services":{"$$id":{"name":"SearXNG","depends_on":["$$id-redis"],"image":"searxng/searxng:$$core_version","volumes":["$$id-searxng:/etc/searxng"],"environment":["SEARXNG_BASE_URL=$$config_searxng_base_url"],"ports":["8080"],"cap_drop":["ALL"],"cap_add":["CHOWN","SETGID","SETUID","DAC_OVERRIDE"],"files":[{"location":"/etc/searxng/settings.yml","content":"\n # see https://docs.searxng.org/admin/engines/settings.html#use-default-settings\n use_default_settings: true\n server:\n secret_key: $$secret_secret_key\n limiter: true\n image_proxy: true\n ui:\n static_use_hash: true\n redis:\n url: redis://:$$secret_redis_password@$$id-redis:6379/0"}]},"$$id-redis":{"name":"Redis","command":"redis-server --requirepass $$secret_redis_password --save \"\" --appendonly \"no\"","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-redis-data:/data"],"environment":["REDIS_PASSWORD=$$secret_redis_password"],"ports":[],"cap_drop":["ALL"],"cap_add":["SETGID","SETUID","DAC_OVERRIDE"]}},"variables":[{"id":"$$config_searxng_base_url","name":"SEARXNG_BASE_URL","label":"SearXNG Base URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_secret_key","name":"SECRET_KEY","label":"Secret Key","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$secret_redis_password","name":"REDIS_PASSWORD","label":"Redis Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v2.0.6","documentation":"https://glitchtip.com/documentation","type":"glitchtip","name":"GlitchTip","description":"Simple, open source error tracking.","labels":["sentry","bugsnag"],"services":{"$$id":{"name":"GlitchTip","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","volumes":[],"environment":["PORT=$$config_port","GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url","EMAIL_HOST=$$config_email_host","EMAIL_PORT=$$config_email_port","EMAIL_HOST_USER=$$config_email_host_user","EMAIL_HOST_PASSWORD=$$secret_email_host_password","EMAIL_USE_TLS=$$config_email_use_tls","EMAIL_USE_SSL=$$config_email_use_ssl","EMAIL_BACKEND=$$config_email_backend","MAILGUN_API_KEY=$$secret_mailgun_api_key","SENDGRID_API_KEY=$$secret_sendgrid_api_key","ENABLE_OPEN_USER_REGISTRATION=$$config_enable_open_user_registration","DJANGO_SUPERUSER_EMAIL=$$config_django_superuser_email","DJANGO_SUPERUSER_PASSWORD=$$secret_django_superuser_password","DJANGO_SUPERUSER_USERNAME=$$config_django_superuser_username","CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency"],"ports":["8000"]},"$$id-worker":{"name":"Celery Worker","command":"./bin/run-celery-with-beat.sh","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","environment":["GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url","CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency"],"ports":[]},"$$id-migrate":{"exclude":true,"name":"Migrate","command":"./manage.py migrate","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","environment":["GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url"],"ports":[]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]},"$$id-redis":{"name":"Redis","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-postgresql-redis-data:/data"],"environment":[],"ports":[]}},"variables":[{"id":"$$config_django_superuser_username","name":"DJANGO_SUPERUSER_USERNAME","label":"Django Superuser Username","defaultValue":"$$generate_username","description":""},{"id":"$$secret_django_superuser_password","name":"DJANGO_SUPERUSER_PASSWORD","label":"Django Superuser Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_port","name":"PORT","label":"GlitchTip Port","defaultValue":"8000","description":""},{"id":"$$config_celery_worker_concurrency","main":"$$id-worker","name":"CELERY_WORKER_CONCURRENCY","label":"Celery Worker Concurrency","defaultValue":"2","description":""},{"id":"$$config_glitchtip_domain","name":"GLITCHTIP_DOMAIN","label":"GlitchTip Domain","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_email_url","name":"EMAIL_URL","label":"SMTP Email URL","defaultValue":"smtp://$$config_email_host_user:$$secret_email_host_password@$$config_email_host:$$config_email_port","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_redis_url","name":"REDIS_URL","label":"Redis URL","defaultValue":"redis://$$id-redis:6379/0","description":""},{"id":"$$config_default_from_email","name":"DEFAULT_FROM_EMAIL","label":"Default Email Address","defaultValue":"noreply@example.com","description":""},{"id":"$$config_email_host","name":"EMAIL_HOST","label":"Email SMTP Host","defaultValue":"","description":""},{"id":"$$config_email_port","name":"EMAIL_PORT","label":"Email SMTP Port","defaultValue":"25","description":""},{"id":"$$config_email_host_user","name":"EMAIL_HOST_USER","label":"Email SMTP User","defaultValue":"","description":""},{"id":"$$secret_email_host_password","name":"EMAIL_HOST_PASSWORD","label":"Email SMTP Password","defaultValue":"","description":""},{"id":"$$config_email_use_tls","name":"EMAIL_USE_TLS","label":"Email Use TLS","defaultValue":"false","description":""},{"id":"$$config_email_use_ssl","name":"EMAIL_USE_SSL","label":"Email Use SSL","defaultValue":"false","description":""},{"id":"$$secret_email_smtp_password","name":"EMAIL_SMTP_PASSWORD","label":"SMTP Password","defaultValue":"","description":""},{"id":"$$config_email_backend","name":"EMAIL_BACKEND","label":"Email Backend","defaultValue":"","description":""},{"id":"$$secret_mailgun_api_key","name":"MAILGUN_API_KEY","label":"Mailgun API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$secret_sendgrid_api_key","name":"SENDGRID_API_KEY","label":"Sendgrid API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_enable_open_user_registration","name":"ENABLE_OPEN_USER_REGISTRATION","label":"Enable Open User Registration","defaultValue":"true","description":""},{"id":"$$config_django_superuser_email","name":"DJANGO_SUPERUSER_EMAIL","label":"Django Superuser Email","defaultValue":"noreply@example.com","description":""},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"glitchtip","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v2.13.0","documentation":"https://hasura.io/docs/latest/index/","type":"hasura","name":"Hasura","description":"Instant realtime GraphQL APIs on any Postgres application, existing or new.","labels":["graphql","database"],"services":{"$$id":{"name":"Hasura","depends_on":["$$id-postgresql"],"image":"hasura/graphql-engine:$$core_version","volumes":[],"environment":["HASURA_GRAPHQL_ENABLE_CONSOLE=$$config_hasura_graphql_enable_console","HASURA_GRAPHQL_METADATA_DATABASE_URL=$$secret_hasura_graphql_metadata_database_url","HASURA_GRAPHQL_ADMIN_SECRET=$$secret_hasura_graphql_admin_secret"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]}},"variables":[{"id":"$$config_hasura_graphql_enable_console","name":"HASURA_GRAPHQL_ENABLE_CONSOLE","label":"Enable Hasura Console","defaultValue":"true","description":""},{"id":"$$secret_hasura_graphql_metadata_database_url","name":"HASURA_GRAPHQL_METADATA_DATABASE_URL","label":"Hasura Metadata Database URL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hasura_graphql_admin_secret","name":"HASURA_GRAPHQL_ADMIN_SECRET","label":"Hasura Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"hasura","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"postgresql-v1.38.0","documentation":"https://umami.is/docs/getting-started","type":"umami-postgresql","name":"Umami","subname":"(PostgreSQL)","description":"A simple, easy to use, self-hosted web analytics solution.","services":{"$$id":{"name":"Umami","depends_on":["$$id-postgresql"],"image":"ghcr.io/umami-software/umami:$$core_version","volumes":[],"environment":["ADMIN_PASSWORD=$$secret_admin_password","DATABASE_URL=$$secret_database_url","DATABASE_TYPE=$$config_database_type","HASH_SALT=$$secret_hash_salt"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[],"files":[{"location":"/docker-entrypoint-initdb.d/schema.postgresql.sql","content":"\n -- CreateTable\n CREATE TABLE \"account\" (\n \"user_id\" SERIAL NOT NULL,\n \"username\" VARCHAR(255) NOT NULL,\n \"password\" VARCHAR(60) NOT NULL,\n \"is_admin\" BOOLEAN NOT NULL DEFAULT false,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"updated_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \n PRIMARY KEY (\"user_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"event\" (\n \"event_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"event_type\" VARCHAR(50) NOT NULL,\n \"event_value\" VARCHAR(50) NOT NULL,\n \n PRIMARY KEY (\"event_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"pageview\" (\n \"view_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"referrer\" VARCHAR(500),\n \n PRIMARY KEY (\"view_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"session\" (\n \"session_id\" SERIAL NOT NULL,\n \"session_uuid\" UUID NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"hostname\" VARCHAR(100),\n \"browser\" VARCHAR(20),\n \"os\" VARCHAR(20),\n \"device\" VARCHAR(20),\n \"screen\" VARCHAR(11),\n \"language\" VARCHAR(35),\n \"country\" CHAR(2),\n \n PRIMARY KEY (\"session_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"website\" (\n \"website_id\" SERIAL NOT NULL,\n \"website_uuid\" UUID NOT NULL,\n \"user_id\" INTEGER NOT NULL,\n \"name\" VARCHAR(100) NOT NULL,\n \"domain\" VARCHAR(500),\n \"share_id\" VARCHAR(64),\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \n PRIMARY KEY (\"website_id\")\n );\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"account.username_unique\" ON \"account\"(\"username\");\n \n -- CreateIndex\n CREATE INDEX \"event_created_at_idx\" ON \"event\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"event_session_id_idx\" ON \"event\"(\"session_id\");\n \n -- CreateIndex\n CREATE INDEX \"event_website_id_idx\" ON \"event\"(\"website_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_created_at_idx\" ON \"pageview\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_session_id_idx\" ON \"pageview\"(\"session_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_idx\" ON \"pageview\"(\"website_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_session_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"session_id\", \"created_at\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"session.session_uuid_unique\" ON \"session\"(\"session_uuid\");\n \n -- CreateIndex\n CREATE INDEX \"session_created_at_idx\" ON \"session\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"session_website_id_idx\" ON \"session\"(\"website_id\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"website.website_uuid_unique\" ON \"website\"(\"website_uuid\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"website.share_id_unique\" ON \"website\"(\"share_id\");\n \n -- CreateIndex\n CREATE INDEX \"website_user_id_idx\" ON \"website\"(\"user_id\");\n \n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"session\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"website\" ADD FOREIGN KEY (\"user_id\") REFERENCES \"account\"(\"user_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);"}]}},"variables":[{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hash_salt","name":"HASH_SALT","label":"Hash Salt","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_database_type","name":"DATABASE_TYPE","label":"Database Type","defaultValue":"postgresql","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"umami","description":""},{"id":"$$secret_admin_password","name":"ADMIN_PASSWORD","label":"Initial Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","ignore":true,"defaultVersion":"postgresql-v1.38.0","documentation":"https://umami.is/docs/getting-started","type":"umami","name":"Umami","subname":"(PostgreSQL)","description":"A simple, easy to use, self-hosted web analytics solution.","services":{"$$id":{"name":"Umami","depends_on":["$$id-postgresql"],"image":"ghcr.io/umami-software/umami:$$core_version","volumes":[],"environment":["ADMIN_PASSWORD=$$secret_admin_password","DATABASE_URL=$$secret_database_url","DATABASE_TYPE=$$config_database_type","HASH_SALT=$$secret_hash_salt"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[],"files":[{"location":"/docker-entrypoint-initdb.d/schema.postgresql.sql","content":"\n -- CreateTable\n CREATE TABLE \"account\" (\n \"user_id\" SERIAL NOT NULL,\n \"username\" VARCHAR(255) NOT NULL,\n \"password\" VARCHAR(60) NOT NULL,\n \"is_admin\" BOOLEAN NOT NULL DEFAULT false,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"updated_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \n PRIMARY KEY (\"user_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"event\" (\n \"event_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"event_type\" VARCHAR(50) NOT NULL,\n \"event_value\" VARCHAR(50) NOT NULL,\n \n PRIMARY KEY (\"event_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"pageview\" (\n \"view_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"referrer\" VARCHAR(500),\n \n PRIMARY KEY (\"view_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"session\" (\n \"session_id\" SERIAL NOT NULL,\n \"session_uuid\" UUID NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"hostname\" VARCHAR(100),\n \"browser\" VARCHAR(20),\n \"os\" VARCHAR(20),\n \"device\" VARCHAR(20),\n \"screen\" VARCHAR(11),\n \"language\" VARCHAR(35),\n \"country\" CHAR(2),\n \n PRIMARY KEY (\"session_id\")\n );\n \n -- CreateTable\n CREATE TABLE \"website\" (\n \"website_id\" SERIAL NOT NULL,\n \"website_uuid\" UUID NOT NULL,\n \"user_id\" INTEGER NOT NULL,\n \"name\" VARCHAR(100) NOT NULL,\n \"domain\" VARCHAR(500),\n \"share_id\" VARCHAR(64),\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \n PRIMARY KEY (\"website_id\")\n );\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"account.username_unique\" ON \"account\"(\"username\");\n \n -- CreateIndex\n CREATE INDEX \"event_created_at_idx\" ON \"event\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"event_session_id_idx\" ON \"event\"(\"session_id\");\n \n -- CreateIndex\n CREATE INDEX \"event_website_id_idx\" ON \"event\"(\"website_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_created_at_idx\" ON \"pageview\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_session_id_idx\" ON \"pageview\"(\"session_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_idx\" ON \"pageview\"(\"website_id\");\n \n -- CreateIndex\n CREATE INDEX \"pageview_website_id_session_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"session_id\", \"created_at\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"session.session_uuid_unique\" ON \"session\"(\"session_uuid\");\n \n -- CreateIndex\n CREATE INDEX \"session_created_at_idx\" ON \"session\"(\"created_at\");\n \n -- CreateIndex\n CREATE INDEX \"session_website_id_idx\" ON \"session\"(\"website_id\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"website.website_uuid_unique\" ON \"website\"(\"website_uuid\");\n \n -- CreateIndex\n CREATE UNIQUE INDEX \"website.share_id_unique\" ON \"website\"(\"share_id\");\n \n -- CreateIndex\n CREATE INDEX \"website_user_id_idx\" ON \"website\"(\"user_id\");\n \n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"session\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n -- AddForeignKey\n ALTER TABLE \"website\" ADD FOREIGN KEY (\"user_id\") REFERENCES \"account\"(\"user_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n \n insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);"}]}},"variables":[{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hash_salt","name":"HASH_SALT","label":"Hash Salt","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_database_type","name":"DATABASE_TYPE","label":"Database Type","defaultValue":"postgresql","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"umami","description":""},{"id":"$$secret_admin_password","name":"ADMIN_PASSWORD","label":"Initial Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"v0.29.1","documentation":"https://docs.meilisearch.com/learn/getting_started/quick_start.html","type":"meilisearch","name":"MeiliSearch","description":"A lightning Fast, Ultra Relevant, and Typo-Tolerant Search Engine.","services":{"$$id":{"name":"MeiliSearch","documentation":"https://docs.meilisearch.com/","depends_on":[],"image":"getmeili/meilisearch:$$core_version","volumes":["$$id-datams:/meili_data/data.ms","$$id-data:/meili_data","$$id-snapshot:/snapshot","$$id-dump:/dumps"],"environment":["MEILI_MASTER_KEY=$$secret_meili_master_key"],"ports":["7700"]}},"variables":[{"id":"$$secret_meili_master_key","name":"MEILI_MASTER_KEY","label":"Master Key","defaultValue":"$$generate_hex(64)","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","ignore":true,"defaultVersion":"latest","documentation":"https://docs.ghost.org","arch":"amd64","type":"ghost-mariadb","name":"Ghost","subname":"(MariaDB)","description":"Free and open source blogging platform.","labels":["cms","blog"],"services":{"$$id":{"name":"Ghost","depends_on":["$$id-mariadb"],"image":"bitnami/ghost:$$core_version","volumes":["$$id-ghost:/bitnami/ghost"],"environment":["url=$$config_url","GHOST_HOST=$$config_ghost_host","GHOST_ENABLE_HTTPS=$$config_ghost_enable_https","GHOST_EMAIL=$$config_ghost_email","GHOST_PASSWORD=$$secret_ghost_password","GHOST_DATABASE_HOST=$$config_ghost_database_host","GHOST_DATABASE_USER=$$config_mariadb_user","GHOST_DATABASE_PASSWORD=$$secret_ghost_database_password","GHOST_DATABASE_NAME=$$config_mariadb_database","GHOST_DATABASE_PORT_NUMBER=3306"],"ports":["2368"]},"$$id-mariadb":{"name":"MariaDB","depends_on":[],"image":"bitnami/mariadb:latest","volumes":["$$id-mariadb:/bitnami/mariadb"],"environment":["MARIADB_USER=$$config_mariadb_user","MARIADB_PASSWORD=$$secret_mariadb_password","MARIADB_DATABASE=$$config_mariadb_database","MARIADB_ROOT_USER=$$config_mariadb_root_user","MARIADB_ROOT_PASSWORD=$$secret_mariadb_root_password"],"ports":[]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_ghost_host","name":"GHOST_HOST","label":"Ghost Host","defaultValue":"$$generate_domain","description":""},{"id":"$$config_ghost_enable_https","name":"GHOST_ENABLE_HTTPS","label":"Ghost Enable HTTPS","defaultValue":"no","description":""},{"id":"$$config_ghost_email","name":"GHOST_EMAIL","label":"Ghost Default Email","defaultValue":"admin@example.com","description":""},{"id":"$$secret_ghost_password","name":"GHOST_PASSWORD","label":"Ghost Default Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_ghost_database_host","name":"GHOST_DATABASE_HOST","label":"Ghost Database Host","defaultValue":"$$id-mariadb","description":""},{"id":"$$config_ghost_database_user","name":"GHOST_DATABASE_USER","label":"MariaDB User","defaultValue":"$$config_mariadb_user","description":""},{"id":"$$secret_ghost_database_password","name":"GHOST_DATABASE_PASSWORD","label":"MariaDB Password","defaultValue":"$$secret_mariadb_password","description":""},{"id":"$$config_ghost_database_name","name":"GHOST_DATABASE_NAME","label":"MariaDB Database","defaultValue":"$$config_mariadb_database","description":""},{"id":"$$config_mariadb_user","name":"MARIADB_USER","label":"MariaDB User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mariadb_password","name":"MARIADB_PASSWORD","label":"MariaDB Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_mariadb_database","name":"MARIADB_DATABASE","label":"MariaDB Database","defaultValue":"ghost","description":""},{"id":"$$config_mariadb_root_user","name":"MARIADB_ROOT_USER","label":"MariaDB Root User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mariadb_root_password","name":"MARIADB_ROOT_PASSWORD","label":"MariaDB Root Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"5.22","documentation":"https://docs.ghost.org","type":"ghost-only","name":"Ghost","subname":"(without Database)","description":"Free and open source blogging platform.","services":{"$$id":{"name":"Ghost","image":"ghost:$$core_version","volumes":["$$id-ghost:/var/lib/ghost/content"],"environment":["url=$$config_url","database__client=$$config_database__client","database__connection__host=$$config_database__connection__host","database__connection__user=$$config_database__connection__user","database__connection__password=$$secret_database__connection__password","database__connection__database=$$config_database__connection__database"],"ports":["2368"]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_database__client","name":"database__client","label":"Database Client","defaultValue":"mysql","description":"","required":true},{"id":"$$config_database__connection__host","name":"database__connection__host","label":"Database Host","defaultValue":"","description":"","required":true,"placeholder":"db.coolify.io"},{"id":"$$config_database__connection__user","name":"database__connection__user","label":"Database User","defaultValue":"","description":"","placeholder":"ghost","required":true},{"id":"$$secret_database__connection__password","name":"database__connection__password","label":"Database Password","defaultValue":"","description":"","placeholder":"superSecretP4ssword","showOnConfiguration":true,"required":true},{"id":"$$config_database__connection__database","name":"database__connection__database","label":"Database Name","defaultValue":"","description":"","placeholder":"ghost_db","required":true}]},{"templateVersion":"1.0.0","defaultVersion":"5.22","documentation":"https://docs.ghost.org","type":"ghost-mysql","name":"Ghost","subname":"(MySQL)","description":"Ghost is a free and open source blogging platform.","services":{"$$id":{"name":"Ghost","depends_on":["$$id-mysql"],"image":"ghost:$$core_version","volumes":["$$id-ghost:/var/lib/ghost/content"],"environment":["url=$$config_url","database__client=$$config_database__client","database__connection__host=$$config_database__connection__host","database__connection__user=$$config_mysql_user","database__connection__password=$$secret_mysql_password","database__connection__database=$$config_mysql_database"],"ports":["2368"]},"$$id-mysql":{"name":"MySQL","depends_on":[],"image":"mysql:8.0","volumes":["$$id-mysql:/var/lib/mysql"],"environment":["MYSQL_USER=$$config_mysql_user","MYSQL_PASSWORD=$$secret_mysql_password","MYSQL_DATABASE=$$config_mysql_database","MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password"],"ports":[]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_database__client","name":"database__client","label":"Database Client","defaultValue":"mysql","description":"","readOnly":true},{"id":"$$config_database__connection__host","name":"database__connection__host","label":"Database Host","defaultValue":"$$id-mysql","description":""},{"id":"$$config_mysql_user","main":"$$id-mysql","name":"MYSQL_USER","label":"MySQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mysql_password","main":"$$id-mysql","name":"MYSQL_PASSWORD","label":"MySQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_mysql_database","main":"$$id-mysql","name":"MYSQL_DATABASE","label":"MySQL Database","defaultValue":"ghost","description":""},{"id":"$$secret_mysql_root_password","name":"MYSQL_ROOT_PASSWORD","label":"MySQL Root Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"php8.1","documentation":"https://wordpress.org/","type":"wordpress","name":"WordPress","subname":"(MySQL)","description":"A content management system based on PHP.","labels":["wordpress","php","cms"],"services":{"$$id":{"name":"WordPress","depends_on":["$$id-mysql"],"image":"wordpress:$$core_version","volumes":["$$id-wordpress-data:/var/www/html"],"environment":["WORDPRESS_DB_HOST=$$config_wordpress_db_host","WORDPRESS_DB_USER=$$config_mysql_user","WORDPRESS_DB_PASSWORD=$$secret_mysql_password","WORDPRESS_DB_NAME=$$config_mysql_database","WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra"],"ports":["80"]},"$$id-mysql":{"name":"MySQL","depends_on":[],"image":"bitnami/mysql:5.7","imageArm":"mysql:8.0","volumes":["$$id-mysql-data:/bitnami/mysql/data"],"volumesArm":["$$id-mysql-data:/var/lib/mysql"],"environment":["MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password","MYSQL_ROOT_USER=$$config_mysql_root_user","MYSQL_DATABASE=$$config_mysql_database","MYSQL_USER=$$config_mysql_user","MYSQL_PASSWORD=$$secret_mysql_password"]}},"variables":[{"id":"$$config_wordpress_db_host","name":"WORDPRESS_DB_HOST","label":"Database Host","defaultValue":"$$id-mysql","description":"","readOnly":true},{"id":"$$config_wordpress_config_extra","name":"WORDPRESS_CONFIG_EXTRA","label":"WordPress Config Extra","defaultValue":"","description":"","type":"textarea","placeholder":"define('WP_DEBUG', true);\ndefine('WP_DEBUG_LOG', true);\ndefine('WP_DEBUG_DISPLAY', false);\n@ini_set('display_errors', 0);\n"},{"id":"$$secret_mysql_root_password","name":"MYSQL_ROOT_PASSWORD","label":"MySQL Root Password","defaultValue":"$$generate_password","description":"","readOnly":true},{"id":"$$config_mysql_root_user","name":"MYSQL_ROOT_USER","label":"MySQL Root User","defaultValue":"$$generate_username","description":"","readOnly":true},{"id":"$$config_mysql_database","name":"MYSQL_DATABASE","label":"MySQL Database","defaultValue":"wordpress","description":"","readOnly":true},{"id":"$$config_mysql_user","name":"MYSQL_USER","label":"MySQL User","defaultValue":"$$generate_username","description":"","readOnly":true},{"id":"$$secret_mysql_password","name":"MYSQL_PASSWORD","label":"MySQL Password","defaultValue":"$$generate_password","description":"","readOnly":true}]},{"templateVersion":"1.0.0","defaultVersion":"php8.1","documentation":"https://wordpress.org/","type":"wordpress-only","name":"WordPress","subname":"(without DB)","description":"A content management system based on PHP.","labels":["wordpress","php","cms"],"services":{"$$id":{"name":"WordPress","image":"wordpress:$$core_version","volumes":["$$id-wordpress-data:/var/www/html"],"environment":["WORDPRESS_DB_HOST=$$config_wordpress_db_host","WORDPRESS_DB_PORT=$$config_wordpress_db_port","WORDPRESS_DB_USER=$$config_wordpress_db_user","WORDPRESS_DB_PASSWORD=$$secret_wordpress_db_password","WORDPRESS_DB_NAME=$$config_wordpress_db_name","WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra"],"ports":["80"]}},"variables":[{"id":"$$config_wordpress_db_host","name":"WORDPRESS_DB_HOST","label":"Database Host","defaultValue":"","description":"","placeholder":"db.coollabs.io","required":true},{"id":"$$config_wordpress_db_port","name":"WORDPRESS_DB_PORT","label":"Database Port","defaultValue":"","description":"","placeholder":"3306","required":true},{"id":"$$config_wordpress_db_user","name":"WORDPRESS_DB_USER","label":"Database User","defaultValue":"","description":"","placeholder":"wordpress","required":true},{"id":"$$secret_wordpress_db_password","name":"WORDPRESS_DB_PASSWORD","label":"Database Password","defaultValue":"","description":"","placeholder":"supers3cr3tpassw0rd!","required":true,"showOnConfiguration":true},{"id":"$$config_wordpress_db_name","name":"WORDPRESS_DB_NAME","label":"Database Name","defaultValue":"","description":"","placeholder":"wordpress","required":true},{"id":"$$config_wordpress_config_extra","name":"WORDPRESS_CONFIG_EXTRA","label":"Extra Config","defaultValue":"","description":"","type":"textarea","placeholder":"define('WP_DEBUG', true);\ndefine('WP_DEBUG_LOG', true);\ndefine('WP_DEBUG_DISPLAY', false);\n@ini_set('display_errors', 0);\n"}]},{"templateVersion":"1.0.0","defaultVersion":"4.7.1","documentation":"https://coder.com/docs/coder-oss/latest","type":"vscodeserver","name":"VSCode Server","description":"Visual Studio Code on a remote server, accessible through the browser.","labels":["vscode","ide"],"services":{"$$id":{"name":"VSCode Server","depends_on":[],"image":"codercom/code-server:$$core_version","volumes":["$$id-config-data:/home/coder/.local/share/code-server","$$id-vscodeserver-data:/home/coder","$$id-keys-directory:/root/.ssh","$$id-theme-and-plugin-directory:/root/.local/share/code-server"],"environment":["PASSWORD=$$secret_password"],"ports":["8080"]}},"variables":[{"id":"$$secret_password","name":"PASSWORD","label":"Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"RELEASE.2022-10-15T19-57-03Z","documentation":"https://min.io/docs/minio","type":"minio","name":"MinIO","description":"A cloud storage server compatible with Amazon S3.","labels":["storage","s3"],"services":{"$$id":{"name":"MinIO","command":"server /data --console-address :9001","depends_on":[],"image":"minio/minio:$$core_version","volumes":["$$id-minio-data:/data","$$id-data-write:/files"],"environment":["MINIO_SERVER_URL=$$config_coolify_fqdn_minio_console","MINIO_BROWSER_REDIRECT_URL=$$config_minio_browser_redirect_url","MINIO_DOMAIN=$$config_minio_domain","MINIO_ROOT_USER=$$config_minio_root_user","MINIO_ROOT_PASSWORD=$$secret_minio_root_password"],"ports":["9000","9001"],"proxy":[{"port":"9000","domain":"$$config_coolify_fqdn_minio_console"},{"port":"9001"}]}},"variables":[{"id":"$$config_coolify_fqdn_minio_console","name":"MINIO_SERVER_URL","label":"MinIO Server URL","defaultValue":"","description":"Specify the URL hostname the MinIO Console should use for connecting to the MinIO Server.","required":true},{"id":"$$config_minio_browser_redirect_url","name":"MINIO_BROWSER_REDIRECT_URL","label":"Browser Redirect URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_minio_domain","name":"MINIO_DOMAIN","label":"Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_minio_root_user","name":"MINIO_ROOT_USER","label":"Root User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_minio_root_password","name":"MINIO_ROOT_PASSWORD","label":"Root User Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"0.21.1","documentation":"https://fider.io/docs","type":"fider","name":"Fider","description":"A platform to collect and organize customer feedback.","labels":["suggestion","feedback"],"services":{"$$id":{"name":"Fider","image":"getfider/fider:$$core_version","depends_on":["$$id-postgresql"],"environment":["BASE_URL=$$config_base_url","DATABASE_URL=$$secret_database_url","JWT_SECRET=$$secret_jwt_secret","EMAIL_NOREPLY=$$config_email_noreply","EMAIL_MAILGUN_API=$$secret_email_mailgun_api","EMAIL_MAILGUN_REGION=$$config_email_mailgun_region","EMAIL_MAILGUN_DOMAIN=$$config_email_mailgun_domain","EMAIL_SMTP_HOST=$$config_email_smtp_host","EMAIL_SMTP_PORT=$$config_email_smtp_port","EMAIL_SMTP_USER=$$config_email_smtp_user","EMAIL_SMTP_PASSWORD=$$secret_email_smtp_password","EMAIL_SMTP_ENABLE_STARTTLS=$$config_email_smtp_enable_starttls"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db?sslmode=disable","description":""},{"id":"$$secret_jwt_secret","name":"JWT_SECRET","label":"JWT Secret","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_email_noreply","name":"EMAIL_NOREPLY","label":"No Reply Email Address","defaultValue":"noreply@example.com","description":""},{"id":"$$secret_email_mailgun_api","name":"EMAIL_MAILGUN_API","label":"Mailgun API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_email_mailgun_region","name":"EMAIL_MAILGUN_REGION","label":"Mailgun Region","defaultValue":"EU","description":""},{"id":"$$config_email_mailgun_domain","name":"EMAIL_MAILGUN_DOMAIN","label":"Mailgun Domain","defaultValue":"","description":""},{"id":"$$config_email_smtp_host","name":"EMAIL_SMTP_HOST","label":"SMTP Host","defaultValue":"","description":""},{"id":"$$config_email_smtp_port","name":"EMAIL_SMTP_PORT","label":"SMTP Port","defaultValue":"587","description":""},{"id":"$$config_email_smtp_user","name":"EMAIL_SMTP_USER","label":"SMTP User","defaultValue":"","description":""},{"id":"$$secret_email_smtp_password","name":"EMAIL_SMTP_PASSWORD","label":"SMTP Password","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_email_smtp_enable_starttls","name":"EMAIL_SMTP_ENABLE_STARTTLS","label":"SMTP Enable StartTLS","defaultValue":"false","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"$$generate_username","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"0.198.1","documentation":"https://docs.n8n.io","type":"n8n","name":"n8n.io","description":"A free and open node based Workflow Automation Tool.","labels":["workflow","automation","ifttt","zapier","nodered"],"services":{"$$id":{"name":"N8n","depends_on":[],"image":"n8nio/n8n:$$core_version","volumes":["$$id-data:/root/.n8n","$$id-data-write:/files","/var/run/docker.sock:/var/run/docker.sock"],"environment":["WEBHOOK_URL=$$config_webhook_url"],"ports":["5678"]}},"variables":[{"id":"$$config_webhook_url","name":"WEBHOOK_URL","label":"Webhook URL","defaultValue":"$$generate_fqdn","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"stable","documentation":"https://plausible.io/doc/","arch":"amd64","type":"plausibleanalytics","name":"Plausible Analytics","description":"A lightweight and open-source website analytics tool.","labels":["analytics","statistics","plausible","gdpr","no-cookie","google analytics"],"services":{"$$id":{"name":"Plausible Analytics","command":"sh -c \"sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run\"","depends_on":["$$id-postgresql","$$id-clickhouse"],"image":"plausible/analytics:$$core_version","environment":["ADMIN_USER_EMAIL=$$config_admin_user_email","ADMIN_USER_NAME=$$config_admin_user_name","ADMIN_USER_PWD=$$secret_admin_user_pwd","BASE_URL=$$config_base_url","SECRET_KEY_BASE=$$secret_secret_key_base","DISABLE_AUTH=$$config_disable_auth","DISABLE_REGISTRATION=$$config_disable_registration","DATABASE_URL=$$secret_database_url","CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url"],"ports":["8000"]},"$$id-postgresql":{"name":"PostgreSQL","image":"bitnami/postgresql:13","volumes":["$$id-postgresql-data:/bitnami/postgresql"],"environment":["POSTGRESQL_PASSWORD=$$secret_postgresql_password","POSTGRESQL_USERNAME=$$config_postgresql_username","POSTGRESQL_DATABASE=$$config_postgresql_database"]},"$$id-clickhouse":{"name":"Clickhouse","volumes":["$$id-clickhouse-data:/var/lib/clickhouse"],"image":"clickhouse/clickhouse-server:22.6-alpine","ulimits":{"nofile":{"soft":262144,"hard":262144}},"files":[{"location":"/etc/clickhouse-server/users.d/logging.xml","content":"warningtrue"},{"location":"/etc/clickhouse-server/config.d/logging.xml","content":"00"},{"location":"/docker-entrypoint-initdb.d/init.query","content":"CREATE DATABASE IF NOT EXISTS plausible;"},{"location":"/docker-entrypoint-initdb.d/init-db.sh","content":"clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query"}]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":"You must set this to the FQDN of the Plausible Analytics instance. This is used to generate the links to the Plausible Analytics instance."},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgresql_username:$$secret_postgresql_password@$$id-postgresql:5432/$$config_postgresql_database","description":""},{"id":"$$secret_clickhouse_database_url","name":"CLICKHOUSE_DATABASE_URL","label":"Database URL for Clickhouse","defaultValue":"http://$$id-clickhouse:8123/plausible","description":""},{"id":"$$config_admin_user_email","name":"ADMIN_USER_EMAIL","label":"Admin Email Address","defaultValue":"admin@example.com","description":"This is the admin email. Please change it."},{"id":"$$config_admin_user_name","name":"ADMIN_USER_NAME","label":"Admin User Name","defaultValue":"$$generate_username","description":"This is the admin username. Please change it."},{"id":"$$secret_admin_user_pwd","name":"ADMIN_USER_PWD","label":"Admin User Password","defaultValue":"$$generate_password","description":"This is the admin password. Please change it.","showOnConfiguration":true},{"id":"$$secret_secret_key_base","name":"SECRET_KEY_BASE","label":"Secret Key Base","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_disable_auth","name":"DISABLE_AUTH","label":"Disable Authentication","defaultValue":"false","description":""},{"id":"$$config_disable_registration","name":"DISABLE_REGISTRATION","label":"Disable Registration","defaultValue":"true","description":""},{"id":"$$config_postgresql_username","main":"$$id-postgresql","name":"POSTGRESQL_USERNAME","label":"PostgreSQL Username","defaultValue":"postgresql","description":""},{"id":"$$secret_postgresql_password","main":"$$id-postgresql","name":"POSTGRESQL_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgresql_database","main":"$$id-postgresql","name":"POSTGRESQL_DATABASE","label":"PostgreSQL Database","defaultValue":"plausible","description":""},{"id":"$$config_scriptName","name":"SCRIPT_NAME","label":"Custom Script Name","defaultValue":"plausible.js","description":"This is the default script name."}]},{"templateVersion":"1.0.0","defaultVersion":"0.98.1","documentation":"https://docs.nocodb.com","type":"nocodb","name":"NocoDB","description":"Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadsheet.","labels":["database","airtable","spreadsheet"],"services":{"$$id":{"name":"NocoDB","image":"nocodb/nocodb:$$core_version","environment":["PORT=$$config_port","NC_DB=$$config_nc_db","DATABASE_URL=$$secret_database_url","NC_PUBLIC_URL=$$config_public_url","NC_AUTH_JWT_SECRET=$$secret_auth_jwt_secret","NC_SENTRY_DSN=$$secret_sentry_dsn","NC_CONNECT_TO_EXTERNAL_DB_DISABLED=$$config_connect_to_external_db_disabled","NC_DISABLE_TELE=$$config_disable_tele"],"volumes":["$$id-data:/usr/app/data"],"ports":["8080"]}},"variables":[{"id":"$$config_nc_db","name":"NC_DB","label":"Database","defaultValue":"","description":"MySQL, PostgreSQL and MSSQL connection urls supported. If absent: A local SQLite will be created in root folder."},{"id":"$$config_port","name":"PORT","label":"Port","defaultValue":"8080","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL","defaultValue":"","description":"JDBC URL Format. Can be used instead of NC_DB. Used in 1-Click Heroku deployment."},{"id":"$$config_public_url","name":"NC_PUBLIC_URL","label":"Public URL","defaultValue":"","description":"Used for sending Email invitations. If absent: Best guess from http request params."},{"id":"$$secret_auth_jwt_secret","name":"NC_AUTH_JWT_SECRET","label":"Auth JWT Secret","defaultValue":"$$generate_hex(64)","description":"JWT secret used for auth and storing other secrets. If absent: A Random secret will be generated."},{"id":"$$secret_sentry_dsn","name":"NC_SENTRY_DSN","label":"Sentry DSN","defaultValue":"","description":"For Sentry monitoring."},{"id":"$$config_connect_to_external_db_disabled","name":"NC_CONNECT_TO_EXTERNAL_DB_DISABLED","label":"Disable External Database","defaultValue":"0","description":"Disable Project creation with external database. (Enter \"1\" to disable)."},{"id":"$$config_disable_tele","name":"NC_DISABLE_TELE","label":"NocoDB Disable Telemetry","defaultValue":"1","description":"Disable telemetry (Enter \"1\" to disable)."}]}] \ No newline at end of file diff --git a/apps/server/src/trpc/context.ts b/apps/server/src/trpc/context.ts new file mode 100644 index 000000000..e1e64c29f --- /dev/null +++ b/apps/server/src/trpc/context.ts @@ -0,0 +1,22 @@ +import type { inferAsyncReturnType } from '@trpc/server'; +import type { CreateFastifyContextOptions } from '@trpc/server/adapters/fastify'; +import jwt from 'jsonwebtoken'; +import { env } from '../env'; +export interface User { + userId: string; + teamId: string; + permission: string; + isAdmin: boolean; + iat: number; +} + +export function createContext({ req }: CreateFastifyContextOptions) { + const token = req.headers.authorization; + let user: User | null = null; + if (token) { + user = jwt.verify(token, env.COOLIFY_SECRET_KEY) as User; + } + return { user }; +} + +export type Context = inferAsyncReturnType; diff --git a/apps/server/src/trpc/index.ts b/apps/server/src/trpc/index.ts new file mode 100644 index 000000000..a5bc3e969 --- /dev/null +++ b/apps/server/src/trpc/index.ts @@ -0,0 +1,23 @@ +import { router } from './trpc'; +import type { Permission } from '@prisma/client'; + +import { + settingsRouter, + authRouter, + dashboardRouter, + applicationsRouter, + servicesRouter, + databasesRouter +} from './routers'; + +export const appRouter = router({ + settings: settingsRouter, + auth: authRouter, + dashboard: dashboardRouter, + applications: applicationsRouter, + services: servicesRouter, + databases: databasesRouter +}); + +export type AppRouter = typeof appRouter; +export type PrismaPermission = Permission; diff --git a/apps/server/src/trpc/routers/applications/index.ts b/apps/server/src/trpc/routers/applications/index.ts new file mode 100644 index 000000000..2674bdd79 --- /dev/null +++ b/apps/server/src/trpc/routers/applications/index.ts @@ -0,0 +1,391 @@ +import { z } from 'zod'; +import fs from 'fs/promises'; +import yaml from 'js-yaml'; +import { privateProcedure, router } from '../../trpc'; +import { prisma } from '../../../prisma'; +import { executeCommand } from '../../../lib/executeCommand'; +import { + checkContainer, + defaultComposeConfiguration, + formatLabelsOnDocker, + removeContainer +} from '../../../lib/docker'; +import { deployApplication, generateConfigHash, getApplicationFromDB } from './lib'; +import cuid from 'cuid'; +import { createDirectories, saveDockerRegistryCredentials } from '../../../lib/common'; + +export const applicationsRouter = router({ + getApplicationById: privateProcedure + .input(z.object({ id: z.string() })) + .query(async ({ ctx, input }) => { + const id: string = input.id; + const teamId = ctx.user?.teamId; + if (!teamId) { + throw { status: 400, message: 'Team not found.' }; + } + const application = await getApplicationFromDB(id, teamId); + return { + success: true, + data: { ...application } + }; + }), + save: privateProcedure + .input( + z.object({ + id: z.string() + }) + ) + .mutation(async ({ ctx, input }) => { + const { id } = input; + const teamId = ctx.user?.teamId; + + // const buildId = await deployApplication(id, teamId); + return { + // buildId + }; + }), + status: privateProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => { + const id: string = input.id; + const teamId = ctx.user?.teamId; + if (!teamId) { + throw { status: 400, message: 'Team not found.' }; + } + let payload = []; + const application: any = await getApplicationFromDB(id, teamId); + if (application?.destinationDockerId) { + if (application.buildPack === 'compose') { + const { stdout: containers } = await executeCommand({ + dockerId: application.destinationDocker.id, + command: `docker ps -a --filter "label=coolify.applicationId=${id}" --format '{{json .}}'` + }); + const containersArray = containers.trim().split('\n'); + if (containersArray.length > 0 && containersArray[0] !== '') { + for (const container of containersArray) { + let isRunning = false; + let isExited = false; + let isRestarting = false; + const containerObj = JSON.parse(container); + const status = containerObj.State; + if (status === 'running') { + isRunning = true; + } + if (status === 'exited') { + isExited = true; + } + if (status === 'restarting') { + isRestarting = true; + } + payload.push({ + name: containerObj.Names, + status: { + isRunning, + isExited, + isRestarting + } + }); + } + } + } else { + let isRunning = false; + let isExited = false; + let isRestarting = false; + const status = await checkContainer({ + dockerId: application.destinationDocker.id, + container: id + }); + if (status?.found) { + isRunning = status.status.isRunning; + isExited = status.status.isExited; + isRestarting = status.status.isRestarting; + payload.push({ + name: id, + status: { + isRunning, + isExited, + isRestarting + } + }); + } + } + } + return payload; + }), + cleanup: privateProcedure.query(async ({ ctx }) => { + const teamId = ctx.user?.teamId; + let applications = await prisma.application.findMany({ + where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, + include: { settings: true, destinationDocker: true, teams: true } + }); + for (const application of applications) { + if ( + !application.buildPack || + !application.destinationDockerId || + !application.branch || + (!application.settings?.isBot && !application?.fqdn) + ) { + if (application?.destinationDockerId && application.destinationDocker?.network) { + const { stdout: containers } = await executeCommand({ + dockerId: application.destinationDocker.id, + command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${application.id} --format '{{json .}}'` + }); + if (containers) { + const containersArray = containers.trim().split('\n'); + for (const container of containersArray) { + const containerObj = JSON.parse(container); + const id = containerObj.ID; + await removeContainer({ id, dockerId: application.destinationDocker.id }); + } + } + } + await prisma.applicationSettings.deleteMany({ where: { applicationId: application.id } }); + await prisma.buildLog.deleteMany({ where: { applicationId: application.id } }); + await prisma.build.deleteMany({ where: { applicationId: application.id } }); + await prisma.secret.deleteMany({ where: { applicationId: application.id } }); + await prisma.applicationPersistentStorage.deleteMany({ + where: { applicationId: application.id } + }); + await prisma.applicationConnectedDatabase.deleteMany({ + where: { applicationId: application.id } + }); + await prisma.application.deleteMany({ where: { id: application.id } }); + } + } + return {}; + }), + stop: privateProcedure.input(z.object({ id: z.string() })).mutation(async ({ ctx, input }) => { + const { id } = input; + const teamId = ctx.user?.teamId; + const application: any = await getApplicationFromDB(id, teamId); + if (application?.destinationDockerId) { + const { id: dockerId } = application.destinationDocker; + if (application.buildPack === 'compose') { + const { stdout: containers } = await executeCommand({ + dockerId: application.destinationDocker.id, + command: `docker ps -a --filter "label=coolify.applicationId=${id}" --format '{{json .}}'` + }); + const containersArray = containers.trim().split('\n'); + if (containersArray.length > 0 && containersArray[0] !== '') { + for (const container of containersArray) { + const containerObj = JSON.parse(container); + await removeContainer({ + id: containerObj.ID, + dockerId: application.destinationDocker.id + }); + } + } + return; + } + const { found } = await checkContainer({ dockerId, container: id }); + if (found) { + await removeContainer({ id, dockerId: application.destinationDocker.id }); + } + } + return {}; + }), + restart: privateProcedure.input(z.object({ id: z.string() })).mutation(async ({ ctx, input }) => { + const { id } = input; + const teamId = ctx.user?.teamId; + let application = await getApplicationFromDB(id, teamId); + if (application?.destinationDockerId) { + const buildId = cuid(); + const { id: dockerId, network } = application.destinationDocker; + const { + dockerRegistry, + secrets, + pullmergeRequestId, + port, + repository, + persistentStorage, + id: applicationId, + buildPack, + exposePort + } = application; + let location = null; + const labels = []; + let image = null; + const envs = [`PORT=${port}`]; + + 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}'`); + } + } + }); + } + const { workdir } = await createDirectories({ repository, buildId }); + + const { stdout: container } = await executeCommand({ + dockerId, + command: `docker container ls --filter 'label=com.docker.compose.service=${id}' --format '{{json .}}'` + }); + const containersArray = container.trim().split('\n'); + for (const container of containersArray) { + const containerObj = formatLabelsOnDocker(container); + image = containerObj[0].Image; + Object.keys(containerObj[0].Labels).forEach(function (key) { + if (key.startsWith('coolify')) { + labels.push(`${key}=${containerObj[0].Labels[key]}`); + } + }); + } + if (dockerRegistry) { + const { url, username, password } = dockerRegistry; + location = await saveDockerRegistryCredentials({ url, username, password, workdir }); + } + + let imageFoundLocally = false; + try { + await executeCommand({ + dockerId, + command: `docker image inspect ${image}` + }); + imageFoundLocally = true; + } catch (error) { + // + } + let imageFoundRemotely = false; + try { + await executeCommand({ + dockerId, + command: `docker ${location ? `--config ${location}` : ''} pull ${image}` + }); + imageFoundRemotely = true; + } catch (error) { + // + } + + if (!imageFoundLocally && !imageFoundRemotely) { + throw { status: 500, message: 'Image not found, cannot restart application.' }; + } + await fs.writeFile(`${workdir}/.env`, envs.join('\n')); + + let envFound = false; + try { + envFound = !!(await fs.stat(`${workdir}/.env`)); + } catch (error) { + // + } + const volumes = + persistentStorage?.map((storage) => { + return `${applicationId}${storage.path.replace(/\//gi, '-')}:${ + buildPack !== 'docker' ? '/app' : '' + }${storage.path}`; + }) || []; + const composeVolumes = volumes.map((volume) => { + return { + [`${volume.split(':')[0]}`]: { + name: volume.split(':')[0] + } + }; + }); + const composeFile = { + version: '3.8', + services: { + [applicationId]: { + image, + container_name: applicationId, + volumes, + env_file: envFound ? [`${workdir}/.env`] : [], + labels, + depends_on: [], + expose: [port], + ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), + ...defaultComposeConfiguration(network) + } + }, + networks: { + [network]: { + external: true + } + }, + volumes: Object.assign({}, ...composeVolumes) + }; + await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile)); + try { + await executeCommand({ dockerId, command: `docker stop -t 0 ${id}` }); + await executeCommand({ dockerId, command: `docker rm ${id}` }); + } catch (error) { + // + } + + await executeCommand({ + dockerId, + command: `docker compose --project-directory ${workdir} up -d` + }); + } + return {}; + }), + deploy: privateProcedure + .input( + z.object({ + id: z.string() + }) + ) + .mutation(async ({ ctx, input }) => { + const { id } = input; + const teamId = ctx.user?.teamId; + const buildId = await deployApplication(id, teamId); + return { + buildId + }; + }), + forceRedeploy: privateProcedure + .input( + z.object({ + id: z.string() + }) + ) + .mutation(async ({ ctx, input }) => { + const { id } = input; + const teamId = ctx.user?.teamId; + const buildId = await deployApplication(id, teamId, true); + return { + buildId + }; + }), + delete: privateProcedure + .input(z.object({ force: z.boolean(), id: z.string() })) + .mutation(async ({ ctx, input }) => { + const { id, force } = input; + const teamId = ctx.user?.teamId; + const application = await prisma.application.findUnique({ + where: { id }, + include: { destinationDocker: true } + }); + if (!force && application?.destinationDockerId && application.destinationDocker?.network) { + const { stdout: containers } = await executeCommand({ + dockerId: application.destinationDocker.id, + command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${id} --format '{{json .}}'` + }); + if (containers) { + const containersArray = containers.trim().split('\n'); + for (const container of containersArray) { + const containerObj = JSON.parse(container); + const id = containerObj.ID; + await removeContainer({ id, dockerId: application.destinationDocker.id }); + } + } + } + await prisma.applicationSettings.deleteMany({ where: { application: { id } } }); + await prisma.buildLog.deleteMany({ where: { applicationId: id } }); + await prisma.build.deleteMany({ where: { applicationId: id } }); + await prisma.secret.deleteMany({ where: { applicationId: id } }); + await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: id } }); + await prisma.applicationConnectedDatabase.deleteMany({ where: { applicationId: id } }); + if (teamId === '0') { + await prisma.application.deleteMany({ where: { id } }); + } else { + await prisma.application.deleteMany({ where: { id, teams: { some: { id: teamId } } } }); + } + return {}; + }) +}); diff --git a/apps/server/src/trpc/routers/applications/lib.ts b/apps/server/src/trpc/routers/applications/lib.ts new file mode 100644 index 000000000..dfedc1bc1 --- /dev/null +++ b/apps/server/src/trpc/routers/applications/lib.ts @@ -0,0 +1,525 @@ +import cuid from 'cuid'; +import crypto from 'node:crypto'; +import { decrypt, isARM } from '../../../lib/common'; + +import { prisma } from '../../../prisma'; + +export async function deployApplication( + id: string, + teamId: string, + forceRebuild: boolean = false +): Promise { + const buildId = cuid(); + const application = await getApplicationFromDB(id, teamId); + if (application) { + if (!application?.configHash) { + await generateConfigHash( + id, + application.buildPack, + application.port, + application.exposePort, + application.installCommand, + application.buildCommand, + application.startCommand + ); + } + await prisma.application.update({ where: { id }, data: { updatedAt: new Date() } }); + if (application.gitSourceId) { + await prisma.build.create({ + data: { + id: buildId, + applicationId: id, + branch: application.branch, + forceRebuild, + destinationDockerId: application.destinationDocker?.id, + gitSourceId: application.gitSource?.id, + githubAppId: application.gitSource?.githubApp?.id, + gitlabAppId: application.gitSource?.gitlabApp?.id, + status: 'queued', + type: 'manual' + } + }); + } else { + await prisma.build.create({ + data: { + id: buildId, + applicationId: id, + branch: 'latest', + forceRebuild, + destinationDockerId: application.destinationDocker?.id, + status: 'queued', + type: 'manual' + } + }); + } + return buildId; + } + throw { status: 500, message: 'Application cannot be deployed.' }; +} +export async function generateConfigHash( + id: string, + buildPack: string, + port: number, + exposePort: number, + installCommand: string, + buildCommand: string, + startCommand: string +): Promise { + const configHash = crypto + .createHash('sha256') + .update( + JSON.stringify({ + buildPack, + port, + exposePort, + installCommand, + buildCommand, + startCommand + }) + ) + .digest('hex'); + return await prisma.application.update({ where: { id }, data: { configHash } }); +} +export async function getApplicationFromDB(id: string, teamId: string) { + let application = await prisma.application.findFirst({ + where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, + include: { + destinationDocker: true, + settings: true, + gitSource: { include: { githubApp: true, gitlabApp: true } }, + secrets: true, + persistentStorage: true, + connectedDatabase: true, + previewApplication: true, + dockerRegistry: true + } + }); + if (!application) { + throw { status: 404, message: 'Application not found.' }; + } + application = decryptApplication(application); + const buildPack = application?.buildPack || null; + const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage(buildPack); + + // Set default build images + if (application && !application.baseImage) { + application.baseImage = baseImage; + } + if (application && !application.baseBuildImage) { + application.baseBuildImage = baseBuildImage; + } + return { ...application, baseBuildImages, baseImages }; +} +function decryptApplication(application: any) { + if (application) { + if (application?.gitSource?.githubApp?.clientSecret) { + application.gitSource.githubApp.clientSecret = + decrypt(application.gitSource.githubApp.clientSecret) || null; + } + if (application?.gitSource?.githubApp?.webhookSecret) { + application.gitSource.githubApp.webhookSecret = + decrypt(application.gitSource.githubApp.webhookSecret) || null; + } + if (application?.gitSource?.githubApp?.privateKey) { + application.gitSource.githubApp.privateKey = + decrypt(application.gitSource.githubApp.privateKey) || null; + } + if (application?.gitSource?.gitlabApp?.appSecret) { + application.gitSource.gitlabApp.appSecret = + decrypt(application.gitSource.gitlabApp.appSecret) || null; + } + if (application?.secrets.length > 0) { + application.secrets = application.secrets.map((s: any) => { + s.value = decrypt(s.value) || null; + return s; + }); + } + + return application; + } +} + +const staticApps = ['static', 'react', 'vuejs', 'svelte', 'gatsby', 'astro', 'eleventy']; +const nodeBased = [ + 'react', + 'preact', + 'vuejs', + 'svelte', + 'gatsby', + 'astro', + 'eleventy', + 'node', + 'nestjs', + 'nuxtjs', + 'nextjs' +]; +export function setDefaultBaseImage( + buildPack: string | null, + deploymentType: string | null = null +) { + const nodeVersions = [ + { + value: 'node:lts', + label: 'node:lts' + }, + { + value: 'node:18', + label: 'node:18' + }, + { + value: 'node:17', + label: 'node:17' + }, + { + value: 'node:16', + label: 'node:16' + }, + { + value: 'node:14', + label: 'node:14' + }, + { + value: 'node:12', + label: 'node:12' + } + ]; + const staticVersions = [ + { + value: 'webdevops/nginx:alpine', + label: 'webdevops/nginx:alpine' + }, + { + value: 'webdevops/apache:alpine', + label: 'webdevops/apache:alpine' + }, + { + value: 'nginx:alpine', + label: 'nginx:alpine' + }, + { + value: 'httpd:alpine', + label: 'httpd:alpine (Apache)' + } + ]; + const rustVersions = [ + { + value: 'rust:latest', + label: 'rust:latest' + }, + { + value: 'rust:1.60', + label: 'rust:1.60' + }, + { + value: 'rust:1.60-buster', + label: 'rust:1.60-buster' + }, + { + value: 'rust:1.60-bullseye', + label: 'rust:1.60-bullseye' + }, + { + value: 'rust:1.60-slim-buster', + label: 'rust:1.60-slim-buster' + }, + { + value: 'rust:1.60-slim-bullseye', + label: 'rust:1.60-slim-bullseye' + }, + { + value: 'rust:1.60-alpine3.14', + label: 'rust:1.60-alpine3.14' + }, + { + value: 'rust:1.60-alpine3.15', + label: 'rust:1.60-alpine3.15' + } + ]; + const phpVersions = [ + { + value: 'webdevops/php-apache:8.2', + label: 'webdevops/php-apache:8.2' + }, + { + value: 'webdevops/php-nginx:8.2', + label: 'webdevops/php-nginx:8.2' + }, + { + value: 'webdevops/php-apache:8.1', + label: 'webdevops/php-apache:8.1' + }, + { + value: 'webdevops/php-nginx:8.1', + label: 'webdevops/php-nginx:8.1' + }, + { + value: 'webdevops/php-apache:8.0', + label: 'webdevops/php-apache:8.0' + }, + { + value: 'webdevops/php-nginx:8.0', + label: 'webdevops/php-nginx:8.0' + }, + { + value: 'webdevops/php-apache:7.4', + label: 'webdevops/php-apache:7.4' + }, + { + value: 'webdevops/php-nginx:7.4', + label: 'webdevops/php-nginx:7.4' + }, + { + value: 'webdevops/php-apache:7.3', + label: 'webdevops/php-apache:7.3' + }, + { + value: 'webdevops/php-nginx:7.3', + label: 'webdevops/php-nginx:7.3' + }, + { + value: 'webdevops/php-apache:7.2', + label: 'webdevops/php-apache:7.2' + }, + { + value: 'webdevops/php-nginx:7.2', + label: 'webdevops/php-nginx:7.2' + }, + { + value: 'webdevops/php-apache:7.1', + label: 'webdevops/php-apache:7.1' + }, + { + value: 'webdevops/php-nginx:7.1', + label: 'webdevops/php-nginx:7.1' + }, + { + value: 'webdevops/php-apache:7.0', + label: 'webdevops/php-apache:7.0' + }, + { + value: 'webdevops/php-nginx:7.0', + label: 'webdevops/php-nginx:7.0' + }, + { + value: 'webdevops/php-apache:5.6', + label: 'webdevops/php-apache:5.6' + }, + { + value: 'webdevops/php-nginx:5.6', + label: 'webdevops/php-nginx:5.6' + }, + { + value: 'webdevops/php-apache:8.2-alpine', + label: 'webdevops/php-apache:8.2-alpine' + }, + { + value: 'webdevops/php-nginx:8.2-alpine', + label: 'webdevops/php-nginx:8.2-alpine' + }, + { + value: 'webdevops/php-apache:8.1-alpine', + label: 'webdevops/php-apache:8.1-alpine' + }, + { + value: 'webdevops/php-nginx:8.1-alpine', + label: 'webdevops/php-nginx:8.1-alpine' + }, + { + value: 'webdevops/php-apache:8.0-alpine', + label: 'webdevops/php-apache:8.0-alpine' + }, + { + value: 'webdevops/php-nginx:8.0-alpine', + label: 'webdevops/php-nginx:8.0-alpine' + }, + { + value: 'webdevops/php-apache:7.4-alpine', + label: 'webdevops/php-apache:7.4-alpine' + }, + { + value: 'webdevops/php-nginx:7.4-alpine', + label: 'webdevops/php-nginx:7.4-alpine' + }, + { + value: 'webdevops/php-apache:7.3-alpine', + label: 'webdevops/php-apache:7.3-alpine' + }, + { + value: 'webdevops/php-nginx:7.3-alpine', + label: 'webdevops/php-nginx:7.3-alpine' + }, + { + value: 'webdevops/php-apache:7.2-alpine', + label: 'webdevops/php-apache:7.2-alpine' + }, + { + value: 'webdevops/php-nginx:7.2-alpine', + label: 'webdevops/php-nginx:7.2-alpine' + }, + { + value: 'webdevops/php-apache:7.1-alpine', + label: 'webdevops/php-apache:7.1-alpine' + }, + { + value: 'php:8.1-fpm', + label: 'php:8.1-fpm' + }, + { + value: 'php:8.0-fpm', + label: 'php:8.0-fpm' + }, + { + value: 'php:8.1-fpm-alpine', + label: 'php:8.1-fpm-alpine' + }, + { + value: 'php:8.0-fpm-alpine', + label: 'php:8.0-fpm-alpine' + } + ]; + const pythonVersions = [ + { + value: 'python:3.10-alpine', + label: 'python:3.10-alpine' + }, + { + value: 'python:3.10-buster', + label: 'python:3.10-buster' + }, + { + value: 'python:3.10-bullseye', + label: 'python:3.10-bullseye' + }, + { + value: 'python:3.10-slim-bullseye', + label: 'python:3.10-slim-bullseye' + }, + { + value: 'python:3.9-alpine', + label: 'python:3.9-alpine' + }, + { + value: 'python:3.9-buster', + label: 'python:3.9-buster' + }, + { + value: 'python:3.9-bullseye', + label: 'python:3.9-bullseye' + }, + { + value: 'python:3.9-slim-bullseye', + label: 'python:3.9-slim-bullseye' + }, + { + value: 'python:3.8-alpine', + label: 'python:3.8-alpine' + }, + { + value: 'python:3.8-buster', + label: 'python:3.8-buster' + }, + { + value: 'python:3.8-bullseye', + label: 'python:3.8-bullseye' + }, + { + value: 'python:3.8-slim-bullseye', + label: 'python:3.8-slim-bullseye' + }, + { + value: 'python:3.7-alpine', + label: 'python:3.7-alpine' + }, + { + value: 'python:3.7-buster', + label: 'python:3.7-buster' + }, + { + value: 'python:3.7-bullseye', + label: 'python:3.7-bullseye' + }, + { + value: 'python:3.7-slim-bullseye', + label: 'python:3.7-slim-bullseye' + } + ]; + const herokuVersions = [ + { + value: 'heroku/builder:22', + label: 'heroku/builder:22' + }, + { + value: 'heroku/buildpacks:20', + label: 'heroku/buildpacks:20' + }, + { + value: 'heroku/builder-classic:22', + label: 'heroku/builder-classic:22' + } + ]; + let payload: any = { + baseImage: null, + baseBuildImage: null, + baseImages: [], + baseBuildImages: [] + }; + 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.baseBuildImage = 'node:lts'; + payload.baseBuildImages = nodeVersions; + } else { + payload.baseImage = 'node:lts'; + payload.baseImages = nodeVersions; + payload.baseBuildImage = 'node:lts'; + payload.baseBuildImages = nodeVersions; + } + } + 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.baseBuildImage = 'node:lts'; + payload.baseBuildImages = nodeVersions; + } + if (buildPack === 'python') { + payload.baseImage = 'python:3.10-alpine'; + payload.baseImages = pythonVersions; + } + if (buildPack === 'rust') { + payload.baseImage = 'rust:latest'; + payload.baseBuildImage = 'rust:latest'; + payload.baseImages = rustVersions; + payload.baseBuildImages = rustVersions; + } + if (buildPack === 'deno') { + 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; + } + 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.baseBuildImage = 'node:18'; + payload.baseBuildImages = nodeVersions; + } + if (buildPack === 'heroku') { + payload.baseImage = 'heroku/buildpacks:20'; + payload.baseImages = herokuVersions; + } + return payload; +} diff --git a/apps/server/src/trpc/routers/auth.ts b/apps/server/src/trpc/routers/auth.ts new file mode 100644 index 000000000..1c431995c --- /dev/null +++ b/apps/server/src/trpc/routers/auth.ts @@ -0,0 +1,178 @@ +import { z } from 'zod'; +import { publicProcedure, router } from '../trpc'; +import { TRPCError } from '@trpc/server'; +import { comparePassword, hashPassword, listSettings, uniqueName } from '../../lib/common'; +import { env } from '../../env'; +import jsonwebtoken from 'jsonwebtoken'; +import { prisma } from '../../prisma'; +import cuid from 'cuid'; + +export const authRouter = router({ + register: publicProcedure + .input( + z.object({ + email: z.string(), + password: z.string() + }) + ) + .mutation(async ({ input }) => { + const { email, password } = input; + const userFound = await prisma.user.findUnique({ + where: { email }, + include: { teams: true, permission: true } + }); + + if (userFound) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'User already exists.' + }); + } + const settings = await listSettings(); + if (!settings?.isRegistrationEnabled) { + throw new TRPCError({ + code: 'FORBIDDEN', + message: 'Registration is disabled.' + }); + } + const usersCount = await prisma.user.count(); + const uid = usersCount === 0 ? '0' : cuid(); + const permission = 'owner'; + const isAdmin = true; + const hashedPassword = await hashPassword(password); + + // Create the first user as the owner + if (usersCount === 0) { + await prisma.user.create({ + data: { + id: uid, + email, + password: hashedPassword, + type: 'email', + teams: { + create: { + id: uid, + name: uniqueName(), + destinationDocker: { connect: { network: 'coolify' } } + } + }, + permission: { create: { teamId: uid, permission } } + }, + include: { teams: true } + }); + await prisma.setting.update({ + where: { id: '0' }, + data: { isRegistrationEnabled: false } + }); + } else { + // Create a new user and team + await prisma.user.create({ + data: { + id: uid, + email, + password: hashedPassword, + type: 'email', + teams: { + create: { + id: uid, + name: uniqueName() + } + }, + permission: { create: { teamId: uid, permission } } + }, + include: { teams: true } + }); + } + const payload = { + userId: uid, + teamId: uid, + permission, + isAdmin + }; + return { + ...payload, + token: jsonwebtoken.sign(payload, env.COOLIFY_SECRET_KEY) + }; + }), + login: publicProcedure + .input( + z.object({ + email: z.string(), + password: z.string() + }) + ) + .mutation(async ({ input }) => { + const { email, password } = input; + const userFound = await prisma.user.findUnique({ + where: { email }, + include: { teams: true, permission: true } + }); + + if (!userFound) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'User already exists.' + }); + } + if (userFound.type === 'email') { + if (userFound.password === 'RESETME') { + const hashedPassword = await hashPassword(password); + if (userFound.updatedAt < new Date(Date.now() - 1000 * 60 * 10)) { + if (userFound.id === '0') { + await prisma.user.update({ + where: { email: userFound.email }, + data: { password: 'RESETME' } + }); + } else { + await prisma.user.update({ + where: { email: userFound.email }, + data: { password: 'RESETTIMEOUT' } + }); + } + } else { + await prisma.user.update({ + where: { email: userFound.email }, + data: { password: hashedPassword } + }); + const payload = { + userId: userFound.id, + teamId: userFound.id, + permission: userFound.permission, + isAdmin: true + }; + return { + ...payload, + token: jsonwebtoken.sign(payload, env.COOLIFY_SECRET_KEY) + }; + } + } + if (!userFound.password) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Something went wrong. Please try again later.' + }); + } + const passwordMatch = comparePassword(password, userFound.password); + if (!passwordMatch) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Incorrect password.' + }); + } + const payload = { + userId: userFound.id, + teamId: userFound.id, + permission: userFound.permission, + isAdmin: true + }; + return { + ...payload, + token: jsonwebtoken.sign(payload, env.COOLIFY_SECRET_KEY) + }; + } + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Not implemented yet.' + }); + }) +}); diff --git a/apps/server/src/trpc/routers/dashboard.ts b/apps/server/src/trpc/routers/dashboard.ts new file mode 100644 index 000000000..273018dc1 --- /dev/null +++ b/apps/server/src/trpc/routers/dashboard.ts @@ -0,0 +1,65 @@ +import { privateProcedure, router } from '../trpc'; +import { listSettings } from '../../lib/common'; +import { prisma } from '../../prisma'; + +export const dashboardRouter = router({ + resources: privateProcedure.query(async ({ ctx }) => { + const id = ctx.user?.teamId === '0' ? undefined : ctx.user?.teamId; + let applications = await prisma.application.findMany({ + where: { teams: { some: { id } } }, + include: { settings: true, destinationDocker: true, teams: true } + }); + const databases = await prisma.database.findMany({ + where: { teams: { some: { id } } }, + include: { settings: true, destinationDocker: true, teams: true } + }); + const services = await prisma.service.findMany({ + where: { teams: { some: { id } } }, + include: { destinationDocker: true, teams: true } + }); + const gitSources = await prisma.gitSource.findMany({ + where: { + OR: [{ teams: { some: { id } } }, { isSystemWide: true }] + }, + include: { teams: true } + }); + const destinations = await prisma.destinationDocker.findMany({ + where: { teams: { some: { id } } }, + include: { teams: true } + }); + const settings = await listSettings(); + let foundUnconfiguredApplication = false; + for (const application of applications) { + if ( + ((!application.buildPack || !application.branch) && !application.simpleDockerfile) || + !application.destinationDockerId || + (!application.settings?.isBot && !application?.fqdn && application.buildPack !== 'compose') + ) { + foundUnconfiguredApplication = true; + } + } + let foundUnconfiguredService = false; + for (const service of services) { + if (!service.fqdn) { + foundUnconfiguredService = true; + } + } + let foundUnconfiguredDatabase = false; + for (const database of databases) { + if (!database.version) { + foundUnconfiguredDatabase = true; + } + } + return { + foundUnconfiguredApplication, + foundUnconfiguredDatabase, + foundUnconfiguredService, + applications, + databases, + services, + gitSources, + destinations, + settings + }; + }) +}); diff --git a/apps/server/src/trpc/routers/databases.ts b/apps/server/src/trpc/routers/databases.ts new file mode 100644 index 000000000..8d4d8a0ee --- /dev/null +++ b/apps/server/src/trpc/routers/databases.ts @@ -0,0 +1,84 @@ +import { z } from 'zod'; +import { privateProcedure, router } from '../trpc'; +import { decrypt } from '../../lib/common'; +import { prisma } from '../../prisma'; +import { executeCommand } from '../../lib/executeCommand'; +import { stopDatabaseContainer, stopTcpHttpProxy } from '../../lib/docker'; + +export const databasesRouter = router({ + status: privateProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => { + const id = input.id; + const teamId = ctx.user?.teamId; + + let isRunning = false; + const database = await prisma.database.findFirst({ + where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, + include: { destinationDocker: true, settings: true } + }); + if (database) { + const { destinationDockerId, destinationDocker } = database; + if (destinationDockerId) { + try { + const { stdout } = await executeCommand({ + dockerId: destinationDocker.id, + command: `docker inspect --format '{{json .State}}' ${id}` + }); + + if (JSON.parse(stdout).Running) { + isRunning = true; + } + } catch (error) { + // + } + } + } + return { + isRunning + }; + }), + cleanup: privateProcedure.query(async ({ ctx }) => { + const teamId = ctx.user?.teamId; + let databases = await prisma.database.findMany({ + where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, + include: { settings: true, destinationDocker: true, teams: true } + }); + for (const database of databases) { + if (!database?.version) { + const { id } = database; + if (database.destinationDockerId) { + const everStarted = await stopDatabaseContainer(database); + if (everStarted) + await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort); + } + await prisma.databaseSettings.deleteMany({ where: { databaseId: id } }); + await prisma.databaseSecret.deleteMany({ where: { databaseId: id } }); + await prisma.database.delete({ where: { id } }); + } + } + return {}; + }), + delete: privateProcedure + .input(z.object({ id: z.string(), force: z.boolean() })) + .mutation(async ({ ctx, input }) => { + const { id, force } = input; + const teamId = ctx.user?.teamId; + const database = await prisma.database.findFirst({ + where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, + include: { destinationDocker: true, settings: true } + }); + if (!force) { + if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); + if (database.rootUserPassword) + database.rootUserPassword = decrypt(database.rootUserPassword); + if (database.destinationDockerId) { + const everStarted = await stopDatabaseContainer(database); + if (everStarted) + await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort); + } + } + await prisma.databaseSettings.deleteMany({ where: { databaseId: id } }); + await prisma.databaseSecret.deleteMany({ where: { databaseId: id } }); + await prisma.database.delete({ where: { id } }); + return {}; + }) +}); diff --git a/apps/server/src/trpc/routers/index.ts b/apps/server/src/trpc/routers/index.ts new file mode 100644 index 000000000..0e16e9c41 --- /dev/null +++ b/apps/server/src/trpc/routers/index.ts @@ -0,0 +1,6 @@ +export * from './auth'; +export * from './dashboard'; +export * from './settings'; +export * from './applications'; +export * from './services'; +export * from './databases'; diff --git a/apps/server/src/trpc/routers/services.ts b/apps/server/src/trpc/routers/services.ts new file mode 100644 index 000000000..6368d60f1 --- /dev/null +++ b/apps/server/src/trpc/routers/services.ts @@ -0,0 +1,171 @@ +import { z } from 'zod'; +import { privateProcedure, router } from '../trpc'; +import { decrypt, getTemplates, removeService } from '../../lib/common'; +import { prisma } from '../../prisma'; +import { executeCommand } from '../../lib/executeCommand'; + +export const servicesRouter = router({ + status: privateProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => { + const id = input.id; + const teamId = ctx.user?.teamId; + if (!teamId) { + throw { status: 400, message: 'Team not found.' }; + } + const service = await getServiceFromDB({ id, teamId }); + const { destinationDockerId } = service; + let payload = {}; + if (destinationDockerId) { + const { stdout: containers } = await executeCommand({ + dockerId: service.destinationDocker.id, + command: `docker ps -a --filter "label=com.docker.compose.project=${id}" --format '{{json .}}'` + }); + if (containers) { + const containersArray = containers.trim().split('\n'); + if (containersArray.length > 0 && containersArray[0] !== '') { + const templates = await getTemplates(); + let template = templates.find((t: { type: string }) => t.type === service.type); + const templateStr = JSON.stringify(template); + if (templateStr) { + template = JSON.parse(templateStr.replaceAll('$$id', service.id)); + } + for (const container of containersArray) { + let isRunning = false; + let isExited = false; + let isRestarting = false; + let isExcluded = false; + const containerObj = JSON.parse(container); + const exclude = template?.services[containerObj.Names]?.exclude; + if (exclude) { + payload[containerObj.Names] = { + status: { + isExcluded: true, + isRunning: false, + isExited: false, + isRestarting: false + } + }; + continue; + } + + const status = containerObj.State; + if (status === 'running') { + isRunning = true; + } + if (status === 'exited') { + isExited = true; + } + if (status === 'restarting') { + isRestarting = true; + } + payload[containerObj.Names] = { + status: { + isExcluded, + isRunning, + isExited, + isRestarting + } + }; + } + } + } + } + return payload; + }), + cleanup: privateProcedure.query(async ({ ctx }) => { + const teamId = ctx.user?.teamId; + let services = await prisma.service.findMany({ + where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, + include: { destinationDocker: true, teams: true } + }); + for (const service of services) { + if (!service.fqdn) { + if (service.destinationDockerId) { + const { stdout: containers } = await executeCommand({ + dockerId: service.destinationDockerId, + command: `docker ps -a --filter 'label=com.docker.compose.project=${service.id}' --format {{.ID}}` + }); + if (containers) { + const containerArray = containers.split('\n'); + if (containerArray.length > 0) { + for (const container of containerArray) { + await executeCommand({ + dockerId: service.destinationDockerId, + command: `docker stop -t 0 ${container}` + }); + await executeCommand({ + dockerId: service.destinationDockerId, + command: `docker rm --force ${container}` + }); + } + } + } + } + await removeService({ id: service.id }); + } + } + }), + delete: privateProcedure + .input(z.object({ force: z.boolean(), id: z.string() })) + .mutation(async ({ input }) => { + // todo: check if user is allowed to delete service + const { id } = input; + await prisma.serviceSecret.deleteMany({ where: { serviceId: id } }); + await prisma.serviceSetting.deleteMany({ where: { serviceId: id } }); + await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } }); + await prisma.meiliSearch.deleteMany({ where: { serviceId: id } }); + await prisma.fider.deleteMany({ where: { serviceId: id } }); + await prisma.ghost.deleteMany({ where: { serviceId: id } }); + await prisma.umami.deleteMany({ where: { serviceId: id } }); + await prisma.hasura.deleteMany({ where: { serviceId: id } }); + await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } }); + await prisma.minio.deleteMany({ where: { serviceId: id } }); + await prisma.vscodeserver.deleteMany({ where: { serviceId: id } }); + await prisma.wordpress.deleteMany({ where: { serviceId: id } }); + await prisma.glitchTip.deleteMany({ where: { serviceId: id } }); + await prisma.moodle.deleteMany({ where: { serviceId: id } }); + await prisma.appwrite.deleteMany({ where: { serviceId: id } }); + await prisma.searxng.deleteMany({ where: { serviceId: id } }); + await prisma.weblate.deleteMany({ where: { serviceId: id } }); + await prisma.taiga.deleteMany({ where: { serviceId: id } }); + + await prisma.service.delete({ where: { id } }); + return {}; + }) +}); + +export async function getServiceFromDB({ + id, + teamId +}: { + id: string; + teamId: string; +}): Promise { + const settings = await prisma.setting.findFirst(); + const body = await prisma.service.findFirst({ + where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, + include: { + destinationDocker: true, + persistentStorage: true, + serviceSecret: true, + serviceSetting: true, + wordpress: true, + plausibleAnalytics: true + } + }); + if (!body) { + return null; + } + // body.type = fixType(body.type); + + if (body?.serviceSecret.length > 0) { + body.serviceSecret = body.serviceSecret.map((s) => { + s.value = decrypt(s.value); + return s; + }); + } + if (body.wordpress) { + body.wordpress.ftpPassword = decrypt(body.wordpress.ftpPassword); + } + + return { ...body, settings }; +} diff --git a/apps/server/src/trpc/routers/settings.ts b/apps/server/src/trpc/routers/settings.ts new file mode 100644 index 000000000..e06de54a9 --- /dev/null +++ b/apps/server/src/trpc/routers/settings.ts @@ -0,0 +1,80 @@ +// import { z } from 'zod'; +import { publicProcedure, privateProcedure, router } from '../trpc'; +import { TRPCError } from '@trpc/server'; +import { getCurrentUser, getTeamInvitation, listSettings, version } from '../../lib/common'; +import { env } from '../../env'; +import type { Permission, TeamInvitation } from '@prisma/client'; +import jsonwebtoken from 'jsonwebtoken'; + +export const settingsRouter = router({ + getBaseSettings: publicProcedure.query(async () => { + const settings = await listSettings(); + return { + success: true, + data: { + isRegistrationEnabled: settings?.isRegistrationEnabled + } + }; + }), + getInstanceSettings: privateProcedure.query(async ({ ctx }) => { + try { + const settings = await listSettings(); + + let isAdmin = false; + let permission = null; + let token = null; + let pendingInvitations: TeamInvitation[] = []; + + if (!settings) { + throw new TRPCError({ + code: 'INTERNAL_SERVER_ERROR', + message: 'An unexpected error occurred, please try again later.' + }); + } + if (ctx.user) { + const currentUser = await getCurrentUser(ctx.user.userId); + if (currentUser) { + const foundPermission = currentUser.permission.find( + (p: Permission) => p.teamId === ctx.user?.teamId + )?.permission; + if (foundPermission) { + permission = foundPermission; + isAdmin = foundPermission === 'owner' || foundPermission === 'admin'; + } + const payload = { + userId: ctx.user?.userId, + teamId: ctx.user?.teamId, + permission, + isAdmin, + iat: Math.floor(Date.now() / 1000) + }; + token = jsonwebtoken.sign(payload, env.COOLIFY_SECRET_KEY); + } + pendingInvitations = await getTeamInvitation(ctx.user.userId); + } + return { + success: true, + data: { + token, + userId: ctx.user?.userId, + teamId: ctx.user?.teamId, + permission, + isAdmin, + ipv4: ctx.user?.teamId ? settings.ipv4 : null, + ipv6: ctx.user?.teamId ? settings.ipv6 : null, + version, + whiteLabeled: env.COOLIFY_WHITE_LABELED === 'true', + whiteLabeledIcon: env.COOLIFY_WHITE_LABELED_ICON, + isRegistrationEnabled: settings.isRegistrationEnabled, + pendingInvitations + } + }; + } catch (error) { + throw new TRPCError({ + code: 'INTERNAL_SERVER_ERROR', + message: 'An unexpected error occurred, please try again later.', + cause: error + }); + } + }) +}); diff --git a/apps/server/src/trpc/trpc.ts b/apps/server/src/trpc/trpc.ts new file mode 100644 index 000000000..79c7c5781 --- /dev/null +++ b/apps/server/src/trpc/trpc.ts @@ -0,0 +1,33 @@ +import { initTRPC, TRPCError } from '@trpc/server'; +import superjson from 'superjson'; +import type { Context } from './context'; + +const t = initTRPC.context().create({ + transformer: superjson, + errorFormatter({ shape }) { + return shape; + } +}); +const logger = t.middleware(async ({ path, type, next }) => { + const start = Date.now(); + const result = await next(); + const durationMs = Date.now() - start; + result.ok + ? console.log('OK request timing:', { path, type, durationMs }) + : console.log('Non-OK request timing', { path, type, durationMs }); + return result; +}); + +const isAdmin = t.middleware(async ({ ctx, next }) => { + if (!ctx.user) { + throw new TRPCError({ code: 'UNAUTHORIZED' }); + } + return next({ + ctx: { + user: ctx.user + } + }); +}); +export const router = t.router; +export const privateProcedure = t.procedure.use(isAdmin); +export const publicProcedure = t.procedure; diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json new file mode 100644 index 000000000..c0c231b4e --- /dev/null +++ b/apps/server/tsconfig.json @@ -0,0 +1,43 @@ +{ + "include": ["src", "server/src/config.ts"], + "exclude": ["node_modules"], + "compilerOptions": { + "module": "commonjs", + "target": "esnext", + "outDir": "dist", + + // [ Basics ] + "strict": true, + "checkJs": true, + "sourceMap": true, + "importHelpers": true, + "removeComments": true, + // "rootDir": "./", + // "declaration": true, + // "declarationMap": true, + // "declarationDir": "types", + + // [ Additional Checks ] + "noUnusedLocals": true, + "noImplicitReturns": true, + "noUnusedParameters": true, + "noImplicitOverride": true, + "allowUnreachableCode": false, + "noUncheckedIndexedAccess": true, + "noFallthroughCasesInSwitch": true, + "noPropertyAccessFromIndexSignature": true, + + // [ Module Resolution ] + "esModuleInterop": true, + "resolveJsonModule": true, + "moduleResolution": "node", + + // [ Advanced ] + // "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + + // [ Build ] + "listFiles": false, + "listEmittedFiles": true + } +} diff --git a/apps/ui/src/lib/store.ts b/apps/ui/src/lib/store.ts index f779eb1dc..493896c70 100644 --- a/apps/ui/src/lib/store.ts +++ b/apps/ui/src/lib/store.ts @@ -23,7 +23,8 @@ interface AppSession { github: string | null, gitlab: string | null, }, - pendingInvitations: Array + pendingInvitations: Array, + isARM: boolean } interface AddToast { type?: "info" | "success" | "error", @@ -52,7 +53,8 @@ export const appSession: Writable = writable({ github: null, gitlab: null }, - pendingInvitations: [] + pendingInvitations: [], + isARM: false }); export const disabledButton: Writable = writable(false); export const isDeploymentEnabled: Writable = writable(false); diff --git a/apps/ui/src/routes/__layout.svelte b/apps/ui/src/routes/__layout.svelte index 2efaa8a4e..d71367a6c 100644 --- a/apps/ui/src/routes/__layout.svelte +++ b/apps/ui/src/routes/__layout.svelte @@ -75,6 +75,7 @@ $appSession.version = baseSettings.version; $appSession.whiteLabeled = baseSettings.whiteLabeled; $appSession.whiteLabeledDetails.icon = baseSettings.whiteLabeledIcon; + $appSession.isARM = baseSettings.isARM; $appSession.pendingInvitations = pendingInvitations; @@ -135,6 +136,10 @@ }); } }); + + let sidedrawerToggler: HTMLInputElement; + + const closeDrawer = () => (sidedrawerToggler.checked = false); @@ -154,13 +159,15 @@ {/if}
- +
{#if $appSession.userId} IAM Settings + Documentation Logout