Merge branch 'restray_i18n' of github.com:restray/coolify into restray_i18n
This commit is contained in:
commit
b4bbd22781
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
yarn lint-staged
|
pnpm lint-staged
|
||||||
|
@ -35,7 +35,8 @@ This is a little list of what you can do to help the project:
|
|||||||
- Copy `.env.template` to `.env` and set the `COOLIFY_APP_ID` environment variable to something cool.
|
- Copy `.env.template` to `.env` and set the `COOLIFY_APP_ID` environment variable to something cool.
|
||||||
- Install dependencies with `pnpm install`.
|
- Install dependencies with `pnpm install`.
|
||||||
- Need to create a local SQlite database with `pnpm db:push`.
|
- Need to create a local SQlite database with `pnpm db:push`.
|
||||||
- This will apply all migrations and seed the database at `db/dev.db`.
|
- This will apply all migrations at `db/dev.db`.
|
||||||
|
- Seed the database with base entities with `pnpm db:seed`
|
||||||
- You can start coding after starting `pnpm dev`.
|
- You can start coding after starting `pnpm dev`.
|
||||||
|
|
||||||
#### How to start after you set up your local fork?
|
#### How to start after you set up your local fork?
|
||||||
|
@ -6,7 +6,7 @@ An open-source & self-hostable Heroku / Netlify alternative.
|
|||||||
|
|
||||||
https://demo.coolify.io/
|
https://demo.coolify.io/
|
||||||
|
|
||||||
(If it is unresponsible, that means someone overloaded the server. 🙃)
|
(If it is unresponsive, that means someone overloaded the server. 🙃)
|
||||||
|
|
||||||
## How to install
|
## How to install
|
||||||
|
|
||||||
|
17
package.json
17
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "coolify",
|
"name": "coolify",
|
||||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||||
"version": "2.2.7",
|
"version": "2.3.0",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev",
|
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev",
|
||||||
@ -30,14 +30,14 @@
|
|||||||
"@sveltejs/kit": "1.0.0-next.303",
|
"@sveltejs/kit": "1.0.0-next.303",
|
||||||
"@types/bcrypt": "5.0.0",
|
"@types/bcrypt": "5.0.0",
|
||||||
"@types/js-cookie": "3.0.1",
|
"@types/js-cookie": "3.0.1",
|
||||||
"@types/js-yaml": "^4.0.5",
|
"@types/js-yaml": "4.0.5",
|
||||||
"@types/node": "17.0.23",
|
"@types/node": "17.0.23",
|
||||||
"@types/node-forge": "1.0.1",
|
"@types/node-forge": "1.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "4.31.1",
|
"@typescript-eslint/eslint-plugin": "4.31.1",
|
||||||
"@typescript-eslint/parser": "4.31.1",
|
"@typescript-eslint/parser": "4.31.1",
|
||||||
"@zerodevx/svelte-toast": "0.7.1",
|
"@zerodevx/svelte-toast": "0.7.1",
|
||||||
"autoprefixer": "10.4.4",
|
"autoprefixer": "10.4.4",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cross-var": "1.1.0",
|
"cross-var": "1.1.0",
|
||||||
"eslint": "7.32.0",
|
"eslint": "7.32.0",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
@ -52,8 +52,8 @@
|
|||||||
"svelte": "3.46.4",
|
"svelte": "3.46.4",
|
||||||
"svelte-check": "2.4.6",
|
"svelte-check": "2.4.6",
|
||||||
"svelte-preprocess": "4.10.4",
|
"svelte-preprocess": "4.10.4",
|
||||||
"svelte-select": "^4.4.7",
|
"svelte-select": "4.4.7",
|
||||||
"sveltekit-i18n": "^2.1.2",
|
"sveltekit-i18n": "2.1.2",
|
||||||
"tailwindcss": "3.0.23",
|
"tailwindcss": "3.0.23",
|
||||||
"ts-node": "10.7.0",
|
"ts-node": "10.7.0",
|
||||||
"tslib": "2.3.1",
|
"tslib": "2.3.1",
|
||||||
@ -68,7 +68,7 @@
|
|||||||
"bullmq": "1.78.1",
|
"bullmq": "1.78.1",
|
||||||
"compare-versions": "4.1.3",
|
"compare-versions": "4.1.3",
|
||||||
"cookie": "0.4.2",
|
"cookie": "0.4.2",
|
||||||
"cooltipz-css": "^2.1.0",
|
"cooltipz-css": "2.1.0",
|
||||||
"cuid": "2.1.8",
|
"cuid": "2.1.8",
|
||||||
"dayjs": "1.11.0",
|
"dayjs": "1.11.0",
|
||||||
"dockerode": "3.3.1",
|
"dockerode": "3.3.1",
|
||||||
@ -79,10 +79,11 @@
|
|||||||
"js-cookie": "3.0.1",
|
"js-cookie": "3.0.1",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"jsonwebtoken": "8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "4.2.0",
|
||||||
"node-forge": "1.3.0",
|
"node-forge": "1.3.0",
|
||||||
|
"p-limit": "4.0.0",
|
||||||
"svelte-kit-cookie-session": "2.1.2",
|
"svelte-kit-cookie-session": "2.1.2",
|
||||||
"tailwindcss-scrollbar": "^0.1.0",
|
"tailwindcss-scrollbar": "0.1.0",
|
||||||
"unique-names-generator": "4.7.1"
|
"unique-names-generator": "4.7.1"
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
|
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@ -8,7 +8,7 @@ specifiers:
|
|||||||
'@sveltejs/kit': 1.0.0-next.303
|
'@sveltejs/kit': 1.0.0-next.303
|
||||||
'@types/bcrypt': 5.0.0
|
'@types/bcrypt': 5.0.0
|
||||||
'@types/js-cookie': 3.0.1
|
'@types/js-cookie': 3.0.1
|
||||||
'@types/js-yaml': ^4.0.5
|
'@types/js-yaml': 4.0.5
|
||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
'@types/node-forge': 1.0.1
|
'@types/node-forge': 1.0.1
|
||||||
'@typescript-eslint/eslint-plugin': 4.31.1
|
'@typescript-eslint/eslint-plugin': 4.31.1
|
||||||
@ -19,8 +19,8 @@ specifiers:
|
|||||||
bullmq: 1.78.1
|
bullmq: 1.78.1
|
||||||
compare-versions: 4.1.3
|
compare-versions: 4.1.3
|
||||||
cookie: 0.4.2
|
cookie: 0.4.2
|
||||||
cooltipz-css: ^2.1.0
|
cooltipz-css: 2.1.0
|
||||||
cross-env: ^7.0.3
|
cross-env: 7.0.3
|
||||||
cross-var: 1.1.0
|
cross-var: 1.1.0
|
||||||
cuid: 2.1.8
|
cuid: 2.1.8
|
||||||
dayjs: 1.11.0
|
dayjs: 1.11.0
|
||||||
@ -37,8 +37,9 @@ specifiers:
|
|||||||
js-yaml: 4.1.0
|
js-yaml: 4.1.0
|
||||||
jsonwebtoken: 8.5.1
|
jsonwebtoken: 8.5.1
|
||||||
lint-staged: 12.3.7
|
lint-staged: 12.3.7
|
||||||
mustache: ^4.2.0
|
mustache: 4.2.0
|
||||||
node-forge: 1.3.0
|
node-forge: 1.3.0
|
||||||
|
p-limit: 4.0.0
|
||||||
postcss: 8.4.12
|
postcss: 8.4.12
|
||||||
prettier: 2.6.1
|
prettier: 2.6.1
|
||||||
prettier-plugin-svelte: 2.6.0
|
prettier-plugin-svelte: 2.6.0
|
||||||
@ -48,10 +49,10 @@ specifiers:
|
|||||||
svelte-check: 2.4.6
|
svelte-check: 2.4.6
|
||||||
svelte-kit-cookie-session: 2.1.2
|
svelte-kit-cookie-session: 2.1.2
|
||||||
svelte-preprocess: 4.10.4
|
svelte-preprocess: 4.10.4
|
||||||
svelte-select: ^4.4.7
|
svelte-select: 4.4.7
|
||||||
sveltekit-i18n: ^2.1.2
|
sveltekit-i18n: 2.1.2
|
||||||
tailwindcss: 3.0.23
|
tailwindcss: 3.0.23
|
||||||
tailwindcss-scrollbar: ^0.1.0
|
tailwindcss-scrollbar: 0.1.0
|
||||||
ts-node: 10.7.0
|
ts-node: 10.7.0
|
||||||
tslib: 2.3.1
|
tslib: 2.3.1
|
||||||
typescript: 4.6.3
|
typescript: 4.6.3
|
||||||
@ -78,6 +79,7 @@ dependencies:
|
|||||||
jsonwebtoken: 8.5.1
|
jsonwebtoken: 8.5.1
|
||||||
mustache: 4.2.0
|
mustache: 4.2.0
|
||||||
node-forge: 1.3.0
|
node-forge: 1.3.0
|
||||||
|
p-limit: 4.0.0
|
||||||
svelte-kit-cookie-session: 2.1.2
|
svelte-kit-cookie-session: 2.1.2
|
||||||
tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.23
|
tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.23
|
||||||
unique-names-generator: 4.7.1
|
unique-names-generator: 4.7.1
|
||||||
@ -4270,6 +4272,16 @@ packages:
|
|||||||
engines: { node: '>=12.20' }
|
engines: { node: '>=12.20' }
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/p-limit/4.0.0:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==
|
||||||
|
}
|
||||||
|
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||||
|
dependencies:
|
||||||
|
yocto-queue: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/p-map/2.1.0:
|
/p-map/2.1.0:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
@ -5714,3 +5726,11 @@ packages:
|
|||||||
}
|
}
|
||||||
engines: { node: '>=6' }
|
engines: { node: '>=6' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/yocto-queue/1.0.0:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
||||||
|
}
|
||||||
|
engines: { node: '>=12.20' }
|
||||||
|
dev: false
|
||||||
|
4
prisma/migrations/20220402135305_python/migration.sql
Normal file
4
prisma/migrations/20220402135305_python/migration.sql
Normal file
@ -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;
|
12
prisma/migrations/20220402210645_meilisearch/migration.sql
Normal file
12
prisma/migrations/20220402210645_meilisearch/migration.sql
Normal file
@ -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");
|
@ -87,6 +87,9 @@ model Application {
|
|||||||
baseDirectory String?
|
baseDirectory String?
|
||||||
publishDirectory String?
|
publishDirectory String?
|
||||||
phpModules String?
|
phpModules String?
|
||||||
|
pythonWSGI String?
|
||||||
|
pythonModule String?
|
||||||
|
pythonVariable String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
settings ApplicationSettings?
|
settings ApplicationSettings?
|
||||||
@ -280,6 +283,7 @@ model Service {
|
|||||||
wordpress Wordpress?
|
wordpress Wordpress?
|
||||||
ghost Ghost?
|
ghost Ghost?
|
||||||
serviceSecret ServiceSecret[]
|
serviceSecret ServiceSecret[]
|
||||||
|
meiliSearch MeiliSearch?
|
||||||
}
|
}
|
||||||
|
|
||||||
model PlausibleAnalytics {
|
model PlausibleAnalytics {
|
||||||
@ -349,3 +353,12 @@ model Ghost {
|
|||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model MeiliSearch {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
masterKey String
|
||||||
|
serviceId String @unique
|
||||||
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
@ -100,10 +100,14 @@ export const setDefaultConfiguration = async (data) => {
|
|||||||
if (buildPack === 'static') port = 80;
|
if (buildPack === 'static') port = 80;
|
||||||
else if (buildPack === 'node') port = 3000;
|
else if (buildPack === 'node') port = 3000;
|
||||||
else if (buildPack === 'php') port = 80;
|
else if (buildPack === 'php') port = 80;
|
||||||
|
else if (buildPack === 'python') port = 8000;
|
||||||
}
|
}
|
||||||
if (!installCommand) installCommand = template?.installCommand || 'yarn install';
|
if (template) {
|
||||||
if (!startCommand) startCommand = template?.startCommand || 'yarn start';
|
if (!installCommand) installCommand = template?.installCommand || 'yarn install';
|
||||||
if (!buildCommand) buildCommand = template?.buildCommand || null;
|
if (!startCommand) startCommand = template?.startCommand || 'yarn start';
|
||||||
|
if (!buildCommand) buildCommand = template?.buildCommand || null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!publishDirectory) publishDirectory = template?.publishDirectory || null;
|
if (!publishDirectory) publishDirectory = template?.publishDirectory || null;
|
||||||
if (baseDirectory) {
|
if (baseDirectory) {
|
||||||
if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`;
|
if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`;
|
||||||
@ -136,7 +140,11 @@ export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, ap
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
await fs.writeFile(`${workdir}/entrypoint.sh`, `chown -R 1000 /app`);
|
await fs.writeFile(`${workdir}/entrypoint.sh`, `chown -R 1000 /app`);
|
||||||
saveBuildLog({ line: 'Copied default configuration file for PHP.', buildId, applicationId });
|
await saveBuildLog({
|
||||||
|
line: 'Copied default configuration file for PHP.',
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
} else if (staticDeployments.includes(buildPack)) {
|
} else if (staticDeployments.includes(buildPack)) {
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
`${workdir}/nginx.conf`,
|
`${workdir}/nginx.conf`,
|
||||||
@ -190,7 +198,7 @@ export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, ap
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
saveBuildLog({ line: 'Copied default configuration file.', buildId, applicationId });
|
await saveBuildLog({ line: 'Copied default configuration file.', buildId, applicationId });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -12,6 +12,7 @@ import php from './php';
|
|||||||
import rust from './rust';
|
import rust from './rust';
|
||||||
import astro from './static';
|
import astro from './static';
|
||||||
import eleventy from './static';
|
import eleventy from './static';
|
||||||
|
import python from './python';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
node,
|
node,
|
||||||
@ -27,5 +28,6 @@ export {
|
|||||||
php,
|
php,
|
||||||
rust,
|
rust,
|
||||||
astro,
|
astro,
|
||||||
eleventy
|
eleventy,
|
||||||
|
python
|
||||||
};
|
};
|
||||||
|
71
src/lib/buildPacks/python.ts
Normal file
71
src/lib/buildPacks/python.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { buildImage } from '$lib/docker';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
|
const {
|
||||||
|
workdir,
|
||||||
|
port,
|
||||||
|
baseDirectory,
|
||||||
|
secrets,
|
||||||
|
pullmergeRequestId,
|
||||||
|
pythonWSGI,
|
||||||
|
pythonModule,
|
||||||
|
pythonVariable
|
||||||
|
} = data;
|
||||||
|
const Dockerfile: Array<string> = [];
|
||||||
|
Dockerfile.push(`FROM ${image}`);
|
||||||
|
Dockerfile.push('WORKDIR /app');
|
||||||
|
Dockerfile.push(`LABEL coolify.image=true`);
|
||||||
|
if (secrets.length > 0) {
|
||||||
|
secrets.forEach((secret) => {
|
||||||
|
if (secret.isBuildSecret) {
|
||||||
|
if (pullmergeRequestId) {
|
||||||
|
if (secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
||||||
|
Dockerfile.push(`RUN pip install gunicorn`);
|
||||||
|
} else if (pythonWSGI?.toLowerCase() === 'uwsgi') {
|
||||||
|
Dockerfile.push(`RUN apk add --no-cache uwsgi-python3`);
|
||||||
|
// Dockerfile.push(`RUN pip install --no-cache-dir uwsgi`)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.stat(`${workdir}${baseDirectory || ''}/requirements.txt`);
|
||||||
|
Dockerfile.push(`COPY .${baseDirectory || ''}/requirements.txt ./`);
|
||||||
|
Dockerfile.push(`RUN pip install --no-cache-dir -r .${baseDirectory || ''}/requirements.txt`);
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
|
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
||||||
|
Dockerfile.push(`CMD gunicorn -w=4 -b=0.0.0.0:8000 ${pythonModule}:${pythonVariable}`);
|
||||||
|
} else if (pythonWSGI?.toLowerCase() === 'uwsgi') {
|
||||||
|
Dockerfile.push(
|
||||||
|
`CMD uwsgi --master -p 4 --http-socket 0.0.0.0:8000 --uid uwsgi --plugins python3 --protocol uwsgi --wsgi ${pythonModule}:${pythonVariable}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Dockerfile.push(`CMD python ${pythonModule}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function (data) {
|
||||||
|
try {
|
||||||
|
const image = 'python:3-alpine';
|
||||||
|
await createDockerfile(data, image);
|
||||||
|
await buildImage(data);
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
@ -80,7 +80,7 @@ export const getTeam = (event) => {
|
|||||||
|
|
||||||
export const getUserDetails = async (event, isAdminRequired = true) => {
|
export const getUserDetails = async (event, isAdminRequired = true) => {
|
||||||
const teamId = getTeam(event);
|
const teamId = getTeam(event);
|
||||||
const userId = event.locals.session.data.userId || null;
|
const userId = event?.locals?.session?.data?.userId || null;
|
||||||
const { permission = 'read' } = await db.prisma.permission.findFirst({
|
const { permission = 'read' } = await db.prisma.permission.findFirst({
|
||||||
where: { teamId, userId },
|
where: { teamId, userId },
|
||||||
select: { permission: true },
|
select: { permission: true },
|
||||||
|
25
src/lib/components/DatabaseLinks.svelte
Normal file
25
src/lib/components/DatabaseLinks.svelte
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<script>
|
||||||
|
export let database;
|
||||||
|
import Clickhouse from './svg/databases/Clickhouse.svelte';
|
||||||
|
import CouchDb from './svg/databases/CouchDB.svelte';
|
||||||
|
import MongoDb from './svg/databases/MongoDB.svelte';
|
||||||
|
import MySql from './svg/databases/MySQL.svelte';
|
||||||
|
import PostgreSql from './svg/databases/PostgreSQL.svelte';
|
||||||
|
import Redis from './svg/databases/Redis.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span class="relative">
|
||||||
|
{#if database.type === 'clickhouse'}
|
||||||
|
<Clickhouse />
|
||||||
|
{:else if database.type === 'couchdb'}
|
||||||
|
<CouchDb />
|
||||||
|
{:else if database.type === 'mongodb'}
|
||||||
|
<MongoDb />
|
||||||
|
{:else if database.type === 'mysql'}
|
||||||
|
<MySql />
|
||||||
|
{:else if database.type === 'postgresql'}
|
||||||
|
<PostgreSql />
|
||||||
|
{:else if database.type === 'redis'}
|
||||||
|
<Redis />
|
||||||
|
{/if}
|
||||||
|
</span>
|
55
src/lib/components/ServiceLinks.svelte
Normal file
55
src/lib/components/ServiceLinks.svelte
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<script>
|
||||||
|
export let service;
|
||||||
|
import Ghost from './svg/services/Ghost.svelte';
|
||||||
|
import LanguageTool from './svg/services/LanguageTool.svelte';
|
||||||
|
import MinIo from './svg/services/MinIO.svelte';
|
||||||
|
import N8n from './svg/services/N8n.svelte';
|
||||||
|
import NocoDb from './svg/services/NocoDB.svelte';
|
||||||
|
import PlausibleAnalytics from './svg/services/PlausibleAnalytics.svelte';
|
||||||
|
import UptimeKuma from './svg/services/UptimeKuma.svelte';
|
||||||
|
import VaultWarden from './svg/services/VaultWarden.svelte';
|
||||||
|
import VsCodeServer from './svg/services/VSCodeServer.svelte';
|
||||||
|
import Wordpress from './svg/services/Wordpress.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if service.type === 'plausibleanalytics'}
|
||||||
|
<a href="https://plausible.io" target="_blank">
|
||||||
|
<PlausibleAnalytics />
|
||||||
|
</a>
|
||||||
|
{:else if service.type === 'nocodb'}
|
||||||
|
<a href="https://nocodb.com" target="_blank">
|
||||||
|
<NocoDb />
|
||||||
|
</a>
|
||||||
|
{:else if service.type === 'minio'}
|
||||||
|
<a href="https://min.io" target="_blank">
|
||||||
|
<MinIo />
|
||||||
|
</a>
|
||||||
|
{:else if service.type === 'vscodeserver'}
|
||||||
|
<a href="https://coder.com" target="_blank">
|
||||||
|
<VsCodeServer />
|
||||||
|
</a>
|
||||||
|
{:else if service.type === 'wordpress'}
|
||||||
|
<a href="https://wordpress.org" target="_blank">
|
||||||
|
<Wordpress />
|
||||||
|
</a>
|
||||||
|
{:else if service.type === 'vaultwarden'}
|
||||||
|
<a href="https://github.com/dani-garcia/vaultwarden" target="_blank">
|
||||||
|
<VaultWarden />
|
||||||
|
</a>
|
||||||
|
{:else if service.type === 'languagetool'}
|
||||||
|
<a href="https://languagetool.org/dev" target="_blank">
|
||||||
|
<LanguageTool />
|
||||||
|
</a>
|
||||||
|
{:else if service.type === 'n8n'}
|
||||||
|
<a href="https://n8n.io" target="_blank">
|
||||||
|
<N8n />
|
||||||
|
</a>
|
||||||
|
{:else if service.type === 'uptimekuma'}
|
||||||
|
<a href="https://github.com/louislam/uptime-kuma" target="_blank">
|
||||||
|
<UptimeKuma />
|
||||||
|
</a>
|
||||||
|
{:else if service.type === 'ghost'}
|
||||||
|
<a href="https://ghost.org" target="_blank">
|
||||||
|
<Ghost />
|
||||||
|
</a>
|
||||||
|
{/if}
|
@ -19,7 +19,7 @@ export const staticDeployments = [
|
|||||||
'astro',
|
'astro',
|
||||||
'eleventy'
|
'eleventy'
|
||||||
];
|
];
|
||||||
export const notNodeDeployments = ['php', 'docker', 'rust'];
|
export const notNodeDeployments = ['php', 'docker', 'rust', 'python'];
|
||||||
|
|
||||||
export function getDomain(domain) {
|
export function getDomain(domain) {
|
||||||
return domain?.replace('https://', '').replace('http://', '');
|
return domain?.replace('https://', '').replace('http://', '');
|
||||||
@ -37,3 +37,9 @@ export function dashify(str: string, options?: any): string {
|
|||||||
.replace(/-{2,}/g, (m) => (options && options.condense ? '-' : m))
|
.replace(/-{2,}/g, (m) => (options && options.condense ? '-' : m))
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function changeQueryParams(buildId) {
|
||||||
|
const queryParams = new URLSearchParams(window.location.search);
|
||||||
|
queryParams.set('buildId', buildId);
|
||||||
|
return history.pushState(null, null, '?' + queryParams.toString());
|
||||||
|
}
|
||||||
|
45
src/lib/components/svg/services/MeiliSearch.svelte
Normal file
45
src/lib/components/svg/services/MeiliSearch.svelte
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let isAbsolute = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 127 74"
|
||||||
|
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
><path
|
||||||
|
d="M.825 73.993l23.244-59.47A21.85 21.85 0 0144.42.625h14.014L35.19 60.096a21.85 21.85 0 01-20.352 13.897H.825z"
|
||||||
|
fill="url(#meilisearch_logo_svg__paint0_linear_0_6)"
|
||||||
|
/><path
|
||||||
|
d="M34.925 73.993l23.243-59.47A21.85 21.85 0 0178.52.626h14.013L69.29 60.096a21.85 21.85 0 01-20.351 13.897H34.925z"
|
||||||
|
fill="url(#meilisearch_logo_svg__paint1_linear_0_6)"
|
||||||
|
/><path
|
||||||
|
d="M69.026 73.993l23.244-59.47A21.85 21.85 0 01112.621.626h14.014l-23.244 59.47a21.851 21.851 0 01-20.352 13.897H69.026z"
|
||||||
|
fill="url(#meilisearch_logo_svg__paint2_linear_0_6)"
|
||||||
|
/><defs
|
||||||
|
><linearGradient
|
||||||
|
id="meilisearch_logo_svg__paint0_linear_0_6"
|
||||||
|
x1="126.635"
|
||||||
|
y1="-4.978"
|
||||||
|
x2="0.825"
|
||||||
|
y2="66.098"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
><stop stop-color="#FF5CAA" /><stop offset="1" stop-color="#FF4E62" /></linearGradient
|
||||||
|
><linearGradient
|
||||||
|
id="meilisearch_logo_svg__paint1_linear_0_6"
|
||||||
|
x1="126.635"
|
||||||
|
y1="-4.978"
|
||||||
|
x2="0.825"
|
||||||
|
y2="66.098"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
><stop stop-color="#FF5CAA" /><stop offset="1" stop-color="#FF4E62" /></linearGradient
|
||||||
|
><linearGradient
|
||||||
|
id="meilisearch_logo_svg__paint2_linear_0_6"
|
||||||
|
x1="126.635"
|
||||||
|
y1="-4.978"
|
||||||
|
x2="0.825"
|
||||||
|
y2="66.098"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
><stop stop-color="#FF5CAA" /><stop offset="1" stop-color="#FF4E62" /></linearGradient
|
||||||
|
></defs
|
||||||
|
></svg
|
||||||
|
>
|
@ -146,6 +146,13 @@ export function findBuildPack(pack, packageManager = 'npm') {
|
|||||||
port: 80
|
port: 80
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (pack === 'python') {
|
||||||
|
return {
|
||||||
|
...metaData,
|
||||||
|
startCommand: null,
|
||||||
|
port: 8000
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
name: 'node',
|
name: 'node',
|
||||||
fancyName: 'Node.js',
|
fancyName: 'Node.js',
|
||||||
@ -249,6 +256,12 @@ export const buildPacks = [
|
|||||||
fancyName: 'Rust',
|
fancyName: 'Rust',
|
||||||
hoverColor: 'hover:bg-pink-700',
|
hoverColor: 'hover:bg-pink-700',
|
||||||
color: 'bg-pink-700'
|
color: 'bg-pink-700'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'python',
|
||||||
|
fancyName: 'Python',
|
||||||
|
hoverColor: 'hover:bg-green-700',
|
||||||
|
color: 'bg-green-700'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
export const scanningTemplates = {
|
export const scanningTemplates = {
|
||||||
|
@ -214,11 +214,15 @@ export async function configureApplication({
|
|||||||
buildCommand,
|
buildCommand,
|
||||||
startCommand,
|
startCommand,
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
publishDirectory
|
publishDirectory,
|
||||||
|
pythonWSGI,
|
||||||
|
pythonModule,
|
||||||
|
pythonVariable
|
||||||
}) {
|
}) {
|
||||||
return await prisma.application.update({
|
return await prisma.application.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
|
name,
|
||||||
buildPack,
|
buildPack,
|
||||||
fqdn,
|
fqdn,
|
||||||
port,
|
port,
|
||||||
@ -227,7 +231,9 @@ export async function configureApplication({
|
|||||||
startCommand,
|
startCommand,
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
publishDirectory,
|
publishDirectory,
|
||||||
name
|
pythonWSGI,
|
||||||
|
pythonModule,
|
||||||
|
pythonVariable
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,9 @@ export function ErrorHandler(e) {
|
|||||||
if (e.message?.includes('git clone')) {
|
if (e.message?.includes('git clone')) {
|
||||||
truncatedError.message = 'git clone failed';
|
truncatedError.message = 'git clone failed';
|
||||||
}
|
}
|
||||||
sentry.captureException(truncatedError);
|
if (!e.message?.includes('Coolify Proxy is not running')) {
|
||||||
|
sentry.captureException(truncatedError);
|
||||||
|
}
|
||||||
const payload = {
|
const payload = {
|
||||||
status: truncatedError.status || 500,
|
status: truncatedError.status || 500,
|
||||||
body: {
|
body: {
|
||||||
@ -195,6 +197,16 @@ export const supportedServiceTypesAndVersions = [
|
|||||||
ports: {
|
ports: {
|
||||||
main: 2368
|
main: 2368
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'meilisearch',
|
||||||
|
fancyName: 'Meilisearch',
|
||||||
|
baseImage: 'getmeili/meilisearch',
|
||||||
|
images: [],
|
||||||
|
versions: ['latest'],
|
||||||
|
ports: {
|
||||||
|
main: 7700
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -22,7 +22,8 @@ export async function getService({ id, teamId }) {
|
|||||||
vscodeserver: true,
|
vscodeserver: true,
|
||||||
wordpress: true,
|
wordpress: true,
|
||||||
ghost: true,
|
ghost: true,
|
||||||
serviceSecret: true
|
serviceSecret: true,
|
||||||
|
meiliSearch: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -50,6 +51,8 @@ export async function getService({ id, teamId }) {
|
|||||||
body.ghost.mariadbRootUserPassword = decrypt(body.ghost.mariadbRootUserPassword);
|
body.ghost.mariadbRootUserPassword = decrypt(body.ghost.mariadbRootUserPassword);
|
||||||
if (body.ghost?.defaultPassword) body.ghost.defaultPassword = decrypt(body.ghost.defaultPassword);
|
if (body.ghost?.defaultPassword) body.ghost.defaultPassword = decrypt(body.ghost.defaultPassword);
|
||||||
|
|
||||||
|
if (body.meiliSearch?.masterKey) body.meiliSearch.masterKey = decrypt(body.meiliSearch.masterKey);
|
||||||
|
|
||||||
if (body?.serviceSecret.length > 0) {
|
if (body?.serviceSecret.length > 0) {
|
||||||
body.serviceSecret = body.serviceSecret.map((s) => {
|
body.serviceSecret = body.serviceSecret.map((s) => {
|
||||||
s.value = decrypt(s.value);
|
s.value = decrypt(s.value);
|
||||||
@ -165,6 +168,15 @@ export async function configureServiceType({ id, type }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (type === 'meilisearch') {
|
||||||
|
const masterKey = encrypt(generatePassword(32));
|
||||||
|
await prisma.service.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
type,
|
||||||
|
meiliSearch: { create: { masterKey } }
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function setServiceVersion({ id, version }) {
|
export async function setServiceVersion({ id, version }) {
|
||||||
@ -191,6 +203,9 @@ export async function updateService({ id, fqdn, name }) {
|
|||||||
export async function updateLanguageToolService({ id, fqdn, name }) {
|
export async function updateLanguageToolService({ id, fqdn, name }) {
|
||||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||||
}
|
}
|
||||||
|
export async function updateMeiliSearchService({ id, fqdn, name }) {
|
||||||
|
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||||
|
}
|
||||||
export async function updateVaultWardenService({ id, fqdn, name }) {
|
export async function updateVaultWardenService({ id, fqdn, name }) {
|
||||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||||
}
|
}
|
||||||
@ -214,6 +229,7 @@ export async function updateGhostService({ id, fqdn, name, mariadbDatabase }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function removeService({ id }) {
|
export async function removeService({ id }) {
|
||||||
|
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.ghost.deleteMany({ where: { serviceId: id } });
|
await prisma.ghost.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } });
|
await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.minio.deleteMany({ where: { serviceId: id } });
|
await prisma.minio.deleteMany({ where: { serviceId: id } });
|
||||||
|
@ -88,12 +88,12 @@ export async function buildImage({
|
|||||||
debug = false
|
debug = false
|
||||||
}) {
|
}) {
|
||||||
if (isCache) {
|
if (isCache) {
|
||||||
saveBuildLog({ line: `Building cache image started.`, buildId, applicationId });
|
await saveBuildLog({ line: `Building cache image started.`, buildId, applicationId });
|
||||||
} else {
|
} else {
|
||||||
saveBuildLog({ line: `Building image started.`, buildId, applicationId });
|
await saveBuildLog({ line: `Building image started.`, buildId, applicationId });
|
||||||
}
|
}
|
||||||
if (!debug && isCache) {
|
if (!debug && isCache) {
|
||||||
saveBuildLog({
|
await saveBuildLog({
|
||||||
line: `Debug turned off. To see more details, allow it in the configuration.`,
|
line: `Debug turned off. To see more details, allow it in the configuration.`,
|
||||||
buildId,
|
buildId,
|
||||||
applicationId
|
applicationId
|
||||||
@ -126,13 +126,17 @@ export async function streamEvents({ stream, docker, buildId, applicationId, deb
|
|||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}
|
}
|
||||||
function onProgress(event) {
|
async function onProgress(event) {
|
||||||
if (event.error) {
|
if (event.error) {
|
||||||
reject(event.error);
|
reject(event.error);
|
||||||
} else if (event.stream) {
|
} else if (event.stream) {
|
||||||
if (event.stream !== '\n') {
|
if (event.stream !== '\n') {
|
||||||
if (debug)
|
if (debug)
|
||||||
saveBuildLog({ line: `${event.stream.replace('\n', '')}`, buildId, applicationId });
|
await saveBuildLog({
|
||||||
|
line: `${event.stream.replace('\n', '')}`,
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ export async function configureHAProxy() {
|
|||||||
isRunning,
|
isRunning,
|
||||||
isHttps,
|
isHttps,
|
||||||
redirectValue,
|
redirectValue,
|
||||||
redirectTo: isWWW ? domain : 'www.' + domain,
|
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain,
|
||||||
updatedAt: updatedAt.getTime()
|
updatedAt: updatedAt.getTime()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -199,7 +199,7 @@ export async function configureHAProxy() {
|
|||||||
isRunning,
|
isRunning,
|
||||||
isHttps,
|
isHttps,
|
||||||
redirectValue,
|
redirectValue,
|
||||||
redirectTo: isWWW ? previewDomain : 'www.' + previewDomain,
|
redirectTo: isWWW ? previewDomain.replace('www.', '') : 'www.' + previewDomain,
|
||||||
updatedAt: updatedAt.getTime()
|
updatedAt: updatedAt.getTime()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -242,7 +242,7 @@ export async function configureHAProxy() {
|
|||||||
isRunning,
|
isRunning,
|
||||||
isHttps,
|
isHttps,
|
||||||
redirectValue,
|
redirectValue,
|
||||||
redirectTo: isWWW ? domain : 'www.' + domain,
|
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain,
|
||||||
updatedAt: updatedAt.getTime()
|
updatedAt: updatedAt.getTime()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -262,7 +262,7 @@ export async function configureHAProxy() {
|
|||||||
domain,
|
domain,
|
||||||
isHttps,
|
isHttps,
|
||||||
redirectValue,
|
redirectValue,
|
||||||
redirectTo: isWWW ? domain : 'www.' + domain
|
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const output = mustache.render(template, data);
|
const output = mustache.render(template, data);
|
||||||
|
@ -10,11 +10,14 @@ export default async function ({
|
|||||||
workdir,
|
workdir,
|
||||||
githubAppId,
|
githubAppId,
|
||||||
repository,
|
repository,
|
||||||
|
apiUrl,
|
||||||
|
htmlUrl,
|
||||||
branch,
|
branch,
|
||||||
buildId
|
buildId
|
||||||
}): Promise<any> {
|
}): Promise<any> {
|
||||||
try {
|
try {
|
||||||
saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId });
|
const url = htmlUrl.replace('https://', '').replace('http://', '');
|
||||||
|
await saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId });
|
||||||
const { privateKey, appId, installationId } = await db.getUniqueGithubApp({ githubAppId });
|
const { privateKey, appId, installationId } = await db.getUniqueGithubApp({ githubAppId });
|
||||||
const githubPrivateKey = privateKey.replace(/\\n/g, '\n').replace(/"/g, '');
|
const githubPrivateKey = privateKey.replace(/\\n/g, '\n').replace(/"/g, '');
|
||||||
|
|
||||||
@ -27,20 +30,20 @@ export default async function ({
|
|||||||
algorithm: 'RS256'
|
algorithm: 'RS256'
|
||||||
});
|
});
|
||||||
const { token } = await got
|
const { token } = await got
|
||||||
.post(`https://api.github.com/app/installations/${installationId}/access_tokens`, {
|
.post(`${apiUrl}/app/installations/${installationId}/access_tokens`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${jwtToken}`,
|
Authorization: `Bearer ${jwtToken}`,
|
||||||
Accept: 'application/vnd.github.machine-man-preview+json'
|
Accept: 'application/vnd.github.machine-man-preview+json'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.json();
|
.json();
|
||||||
saveBuildLog({
|
await saveBuildLog({
|
||||||
line: `Cloning ${repository}:${branch} branch.`,
|
line: `Cloning ${repository}:${branch} branch.`,
|
||||||
buildId,
|
buildId,
|
||||||
applicationId
|
applicationId
|
||||||
});
|
});
|
||||||
await asyncExecShell(
|
await asyncExecShell(
|
||||||
`git clone -q -b ${branch} https://x-access-token:${token}@github.com/${repository}.git ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && cd ..`
|
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && cd ..`
|
||||||
);
|
);
|
||||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
||||||
return commit.replace('\n', '');
|
return commit.replace('\n', '');
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
import { asyncExecShell, saveBuildLog } from '$lib/common';
|
import { asyncExecShell, saveBuildLog } from '$lib/common';
|
||||||
import { ErrorHandler } from '$lib/database';
|
|
||||||
|
|
||||||
export default async function ({
|
export default async function ({
|
||||||
applicationId,
|
applicationId,
|
||||||
debug,
|
|
||||||
workdir,
|
workdir,
|
||||||
repodir,
|
repodir,
|
||||||
|
htmlUrl,
|
||||||
repository,
|
repository,
|
||||||
branch,
|
branch,
|
||||||
buildId,
|
buildId,
|
||||||
privateSshKey
|
privateSshKey
|
||||||
}): Promise<any> {
|
}): Promise<any> {
|
||||||
saveBuildLog({ line: 'GitLab importer started.', buildId, applicationId });
|
const url = htmlUrl.replace('https://', '').replace('http://', '');
|
||||||
|
await saveBuildLog({ line: 'GitLab importer started.', buildId, applicationId });
|
||||||
await asyncExecShell(`echo '${privateSshKey}' > ${repodir}/id.rsa`);
|
await asyncExecShell(`echo '${privateSshKey}' > ${repodir}/id.rsa`);
|
||||||
await asyncExecShell(`chmod 600 ${repodir}/id.rsa`);
|
await asyncExecShell(`chmod 600 ${repodir}/id.rsa`);
|
||||||
|
|
||||||
saveBuildLog({
|
await saveBuildLog({
|
||||||
line: `Cloning ${repository}:${branch} branch.`,
|
line: `Cloning ${repository}:${branch} branch.`,
|
||||||
buildId,
|
buildId,
|
||||||
applicationId
|
applicationId
|
||||||
});
|
});
|
||||||
|
|
||||||
await asyncExecShell(
|
await asyncExecShell(
|
||||||
`git clone -q -b ${branch} git@gitlab.com:${repository}.git --config core.sshCommand="ssh -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git submodule update --init --recursive && cd ..`
|
`git clone -q -b ${branch} git@${url}:${repository}.git --config core.sshCommand="ssh -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git submodule update --init --recursive && cd ..`
|
||||||
);
|
);
|
||||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
||||||
return commit.replace('\n', '');
|
return commit.replace('\n', '');
|
||||||
|
@ -51,7 +51,10 @@ export default async function (job) {
|
|||||||
pullmergeRequestId = null,
|
pullmergeRequestId = null,
|
||||||
sourceBranch = null,
|
sourceBranch = null,
|
||||||
settings,
|
settings,
|
||||||
persistentStorage
|
persistentStorage,
|
||||||
|
pythonWSGI,
|
||||||
|
pythonModule,
|
||||||
|
pythonVariable
|
||||||
} = job.data;
|
} = job.data;
|
||||||
const { debug } = settings;
|
const { debug } = settings;
|
||||||
|
|
||||||
@ -114,6 +117,7 @@ export default async function (job) {
|
|||||||
branch,
|
branch,
|
||||||
buildId,
|
buildId,
|
||||||
apiUrl: gitSource.apiUrl,
|
apiUrl: gitSource.apiUrl,
|
||||||
|
htmlUrl: gitSource.htmlUrl,
|
||||||
projectId,
|
projectId,
|
||||||
deployKeyId: gitSource.gitlabApp?.deployKeyId || null,
|
deployKeyId: gitSource.gitlabApp?.deployKeyId || null,
|
||||||
privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null
|
privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null
|
||||||
@ -127,7 +131,7 @@ export default async function (job) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.prisma.build.update({ where: { id: buildId }, data: { commit } });
|
await db.prisma.build.update({ where: { id: buildId }, data: { commit } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
@ -157,7 +161,7 @@ export default async function (job) {
|
|||||||
});
|
});
|
||||||
deployNeeded = true;
|
deployNeeded = true;
|
||||||
if (configHash) {
|
if (configHash) {
|
||||||
saveBuildLog({ line: 'Configuration changed.', buildId, applicationId });
|
await saveBuildLog({ line: 'Configuration changed.', buildId, applicationId });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
deployNeeded = false;
|
deployNeeded = false;
|
||||||
@ -200,16 +204,19 @@ export default async function (job) {
|
|||||||
startCommand,
|
startCommand,
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
secrets,
|
secrets,
|
||||||
phpModules
|
phpModules,
|
||||||
|
pythonWSGI,
|
||||||
|
pythonModule,
|
||||||
|
pythonVariable
|
||||||
});
|
});
|
||||||
else {
|
else {
|
||||||
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.`);
|
throw new Error(`Build pack ${buildPack} not found.`);
|
||||||
}
|
}
|
||||||
deployNeeded = true;
|
deployNeeded = true;
|
||||||
} else {
|
} else {
|
||||||
deployNeeded = false;
|
deployNeeded = false;
|
||||||
saveBuildLog({ line: 'Nothing changed.', buildId, applicationId });
|
await saveBuildLog({ line: 'Nothing changed.', buildId, applicationId });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deploy to Docker Engine
|
// Deploy to Docker Engine
|
||||||
@ -259,15 +266,7 @@ export default async function (job) {
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
saveBuildLog({ line: 'Deployment started.', buildId, applicationId });
|
await saveBuildLog({ line: 'Deployment started.', buildId, applicationId });
|
||||||
// for await (const volume of volumes) {
|
|
||||||
// const id = volume.split(':')[0];
|
|
||||||
// try {
|
|
||||||
// await asyncExecShell(`DOCKER_HOST=${host} docker volume inspect ${id}`);
|
|
||||||
// } catch (error) {
|
|
||||||
// await asyncExecShell(`DOCKER_HOST=${host} docker volume create ${id}`);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
const composeVolumes = volumes.map((volume) => {
|
const composeVolumes = volumes.map((volume) => {
|
||||||
return {
|
return {
|
||||||
[`${volume.split(':')[0]}`]: {
|
[`${volume.split(':')[0]}`]: {
|
||||||
@ -300,19 +299,12 @@ export default async function (job) {
|
|||||||
await asyncExecShell(
|
await asyncExecShell(
|
||||||
`DOCKER_HOST=${host} docker compose --project-directory ${workdir} up -d`
|
`DOCKER_HOST=${host} docker compose --project-directory ${workdir} up -d`
|
||||||
);
|
);
|
||||||
|
await saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
|
||||||
// const { stderr } = await asyncExecShell(
|
|
||||||
// `DOCKER_HOST=${host} docker run ${envFound && `--env-file=${workdir}/.env`} ${labels.join(
|
|
||||||
// ' '
|
|
||||||
// )} --name ${imageId} --network ${docker.network} --restart always ${volumes.length > 0 ? volumes : ''
|
|
||||||
// } -d ${applicationId}:${tag}`
|
|
||||||
// );
|
|
||||||
saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
saveBuildLog({ line: error, buildId, applicationId });
|
await saveBuildLog({ line: error, buildId, applicationId });
|
||||||
sentry.captureException(error);
|
sentry.captureException(error);
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
|
await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,12 +135,12 @@ buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => {
|
|||||||
const workdir = `/tmp/build-sources/${job.data.repository}`;
|
const workdir = `/tmp/build-sources/${job.data.repository}`;
|
||||||
if (!dev) await asyncExecShell(`rm -fr ${workdir}`);
|
if (!dev) await asyncExecShell(`rm -fr ${workdir}`);
|
||||||
}
|
}
|
||||||
saveBuildLog({
|
await saveBuildLog({
|
||||||
line: 'Failed to deploy!',
|
line: 'Failed to deploy!',
|
||||||
buildId: job.data.build_id,
|
buildId: job.data.build_id,
|
||||||
applicationId: job.data.id
|
applicationId: job.data.id
|
||||||
});
|
});
|
||||||
saveBuildLog({
|
await saveBuildLog({
|
||||||
line: `Reason: ${failedReason.toString()}`,
|
line: `Reason: ${failedReason.toString()}`,
|
||||||
buildId: job.data.build_id,
|
buildId: job.data.build_id,
|
||||||
applicationId: job.data.id
|
applicationId: job.data.id
|
||||||
|
@ -89,7 +89,6 @@
|
|||||||
try {
|
try {
|
||||||
const { buildId } = await post(`/applications/${id}/deploy.json`, { ...application });
|
const { buildId } = await post(`/applications/${id}/deploy.json`, { ...application });
|
||||||
toast.push($t('application.deployment_queued'));
|
toast.push($t('application.deployment_queued'));
|
||||||
console.log($page.url);
|
|
||||||
if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
|
if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
|
||||||
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
|
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,6 +5,7 @@ import { checkContainer } from '$lib/haproxy';
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import jsonwebtoken from 'jsonwebtoken';
|
import jsonwebtoken from 'jsonwebtoken';
|
||||||
import { get as getRequest } from '$lib/api';
|
import { get as getRequest } from '$lib/api';
|
||||||
|
import { setDefaultConfiguration } from '$lib/buildPacks/common';
|
||||||
|
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
@ -52,12 +53,23 @@ export const post: RequestHandler = async (event) => {
|
|||||||
buildCommand,
|
buildCommand,
|
||||||
startCommand,
|
startCommand,
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
publishDirectory
|
publishDirectory,
|
||||||
|
pythonWSGI,
|
||||||
|
pythonModule,
|
||||||
|
pythonVariable
|
||||||
} = await event.request.json();
|
} = await event.request.json();
|
||||||
|
|
||||||
if (port) port = Number(port);
|
if (port) port = Number(port);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const defaultConfiguration = await setDefaultConfiguration({
|
||||||
|
buildPack,
|
||||||
|
port,
|
||||||
|
installCommand,
|
||||||
|
startCommand,
|
||||||
|
buildCommand,
|
||||||
|
publishDirectory,
|
||||||
|
baseDirectory
|
||||||
|
});
|
||||||
await db.configureApplication({
|
await db.configureApplication({
|
||||||
id,
|
id,
|
||||||
buildPack,
|
buildPack,
|
||||||
@ -68,7 +80,11 @@ export const post: RequestHandler = async (event) => {
|
|||||||
buildCommand,
|
buildCommand,
|
||||||
startCommand,
|
startCommand,
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
publishDirectory
|
publishDirectory,
|
||||||
|
pythonWSGI,
|
||||||
|
pythonModule,
|
||||||
|
pythonVariable,
|
||||||
|
...defaultConfiguration
|
||||||
});
|
});
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
import { page, session } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import Select from 'svelte-select';
|
||||||
|
|
||||||
import Explainer from '$lib/components/Explainer.svelte';
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
import Setting from '$lib/components/Setting.svelte';
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
@ -58,6 +59,23 @@
|
|||||||
let previews = application.settings.previews;
|
let previews = application.settings.previews;
|
||||||
let dualCerts = application.settings.dualCerts;
|
let dualCerts = application.settings.dualCerts;
|
||||||
let autodeploy = application.settings.autodeploy;
|
let autodeploy = application.settings.autodeploy;
|
||||||
|
|
||||||
|
let wsgis = [
|
||||||
|
{
|
||||||
|
value: 'None',
|
||||||
|
label: 'None'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'Gunicorn',
|
||||||
|
label: 'Gunicorn'
|
||||||
|
}
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// value: 'uWSGI',
|
||||||
|
// label: 'uWSGI'
|
||||||
|
// }
|
||||||
|
];
|
||||||
|
|
||||||
if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
|
if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
|
||||||
application.fqdn = `http://${cuid()}.demo.coolify.io`;
|
application.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||||
}
|
}
|
||||||
@ -112,7 +130,7 @@
|
|||||||
await post(`/applications/${id}.json`, { ...application });
|
await post(`/applications/${id}.json`, { ...application });
|
||||||
return window.location.reload();
|
return window.location.reload();
|
||||||
} catch ({ error }) {
|
} catch ({ error }) {
|
||||||
if (error.startsWith($t('application.dns_not_set_partial_error'))) {
|
if (error?.startsWith($t('application.dns_not_set_partial_error'))) {
|
||||||
forceSave = true;
|
forceSave = true;
|
||||||
}
|
}
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
@ -120,12 +138,19 @@
|
|||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function selectWSGI(event) {
|
||||||
|
application.pythonWSGI = event.detail.value;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex items-center space-x-2 p-5 px-6 font-bold">
|
<div class="flex items-center space-x-2 p-5 px-6 font-bold">
|
||||||
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
<div class="-mb-5 flex-col">
|
||||||
{application.name}
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
||||||
|
Configuration
|
||||||
|
</div>
|
||||||
|
<span class="text-xs">{application.name} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if application.fqdn}
|
{#if application.fqdn}
|
||||||
<a
|
<a
|
||||||
href={application.fqdn}
|
href={application.fqdn}
|
||||||
@ -300,7 +325,7 @@
|
|||||||
>
|
>
|
||||||
{#if browser && window.location.hostname === 'demo.coolify.io'}
|
{#if browser && window.location.hostname === 'demo.coolify.io'}
|
||||||
<Explainer
|
<Explainer
|
||||||
text="<span class='text-white font-bold'>You can use the predefined random domain name or enter your own domain name.</span>"
|
text="<span class='text-white font-bold'>You can use the predefined random url name or enter your own domain name.</span>"
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<Explainer text={$t('application.https_explainer')} />
|
<Explainer text={$t('application.https_explainer')} />
|
||||||
@ -328,6 +353,39 @@
|
|||||||
on:click={() => !isRunning && changeSettings('dualCerts')}
|
on:click={() => !isRunning && changeSettings('dualCerts')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{#if application.buildPack === 'python'}
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<label for="pythonModule" class="text-base font-bold text-stone-100">WSGI</label>
|
||||||
|
<div class="custom-select-wrapper">
|
||||||
|
<Select id="wsgi" items={wsgis} on:select={selectWSGI} value={application.pythonWSGI} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<label for="pythonModule" class="text-base font-bold text-stone-100">Module</label>
|
||||||
|
<input
|
||||||
|
readonly={!$session.isAdmin}
|
||||||
|
name="pythonModule"
|
||||||
|
id="pythonModule"
|
||||||
|
required
|
||||||
|
bind:value={application.pythonModule}
|
||||||
|
placeholder={application.pythonWSGI?.toLowerCase() !== 'gunicorn' ? 'main.py' : 'main'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if application.pythonWSGI?.toLowerCase() === 'gunicorn'}
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<label for="pythonVariable" class="text-base font-bold text-stone-100">Variable</label>
|
||||||
|
<input
|
||||||
|
readonly={!$session.isAdmin}
|
||||||
|
name="pythonVariable"
|
||||||
|
id="pythonVariable"
|
||||||
|
required
|
||||||
|
bind:value={application.pythonVariable}
|
||||||
|
placeholder="default: app"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
{#if !staticDeployments.includes(application.buildPack)}
|
{#if !staticDeployments.includes(application.buildPack)}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="port" class="text-base font-bold text-stone-100">{$t('forms.port')}</label>
|
<label for="port" class="text-base font-bold text-stone-100">{$t('forms.port')}</label>
|
||||||
@ -336,7 +394,7 @@
|
|||||||
name="port"
|
name="port"
|
||||||
id="port"
|
id="port"
|
||||||
bind:value={application.port}
|
bind:value={application.port}
|
||||||
placeholder="{$t('forms.default')}: 3000"
|
placeholder="{$t('forms.default')}: 'python' ? '8000' : '3000'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { dateOptions, getDomain } from '$lib/components/common';
|
import { changeQueryParams, dateOptions, getDomain } from '$lib/components/common';
|
||||||
|
|
||||||
import BuildLog from './_BuildLog.svelte';
|
import BuildLog from './_BuildLog.svelte';
|
||||||
import { get } from '$lib/api';
|
import { get } from '$lib/api';
|
||||||
@ -80,17 +80,81 @@
|
|||||||
noMoreBuilds = true;
|
noMoreBuilds = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function loadBuild(build) {
|
function loadBuild(build) {
|
||||||
buildId = build;
|
buildId = build;
|
||||||
await goto(`/applications/${id}/logs/build?buildId=${buildId}`);
|
return changeQueryParams(buildId);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div class="flex items-center space-x-2 p-5 px-6 font-bold">
|
||||||
<div class="mr-4 text-2xl tracking-tight">
|
<div class="-mb-5 flex-col">
|
||||||
{$t('application.build.build_logs_of')}
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">$t('application.build_logs')}</div>
|
||||||
<a href={application.fqdn} target="_blank">{getDomain(application.fqdn)}</a>
|
<span class="text-xs">{application.name} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if application.fqdn}
|
||||||
|
<a
|
||||||
|
href={application.fqdn}
|
||||||
|
target="_blank"
|
||||||
|
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
|
<polyline points="15 4 20 4 20 9" />
|
||||||
|
</svg></a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
<a
|
||||||
|
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||||
|
target="_blank"
|
||||||
|
class="w-10"
|
||||||
|
>
|
||||||
|
{#if application.gitSource?.type === 'gitlab'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||||
|
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||||
|
/><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||||
|
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else if application.gitSource?.type === 'github'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<g fill="#ffffff"
|
||||||
|
><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||||
|
/><path
|
||||||
|
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||||
|
/></g
|
||||||
|
>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="block flex-row justify-start space-x-2 px-5 pt-6 sm:px-10 md:flex">
|
<div class="block flex-row justify-start space-x-2 px-5 pt-6 sm:px-10 md:flex">
|
||||||
<div class="mb-4 min-w-[16rem] space-y-2 md:mb-0 ">
|
<div class="mb-4 min-w-[16rem] space-y-2 md:mb-0 ">
|
||||||
@ -134,11 +198,14 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-2">
|
{#if !noMoreBuilds}
|
||||||
<button disabled={noMoreBuilds} class="w-full" on:click={loadMoreBuilds}
|
{#if buildCount > 5}
|
||||||
>{$t('application.build.load_more')}</button
|
<div class="flex space-x-2">
|
||||||
>
|
<button disabled={noMoreBuilds} class="w-full" on:click={loadMoreBuilds}>{$t('application.build.load_more')}</button
|
||||||
</div>
|
>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 md:w-96">
|
<div class="flex-1 md:w-96">
|
||||||
{#if buildId}
|
{#if buildId}
|
||||||
|
@ -69,16 +69,83 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div class="flex items-center space-x-2 p-5 px-6 font-bold">
|
||||||
<div class="mr-4 text-2xl tracking-tight">
|
<div class="-mb-5 flex-col">
|
||||||
Application logs of <a href={application.fqdn} target="_blank">{getDomain(application.fqdn)}</a>
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
||||||
|
Application Logs
|
||||||
|
</div>
|
||||||
|
<span class="text-xs">{application.name} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if application.fqdn}
|
||||||
|
<a
|
||||||
|
href={application.fqdn}
|
||||||
|
target="_blank"
|
||||||
|
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
|
<polyline points="15 4 20 4 20 9" />
|
||||||
|
</svg></a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
<a
|
||||||
|
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||||
|
target="_blank"
|
||||||
|
class="w-10"
|
||||||
|
>
|
||||||
|
{#if application.gitSource?.type === 'gitlab'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||||
|
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||||
|
/><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||||
|
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else if application.gitSource?.type === 'github'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<g fill="#ffffff"
|
||||||
|
><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||||
|
/><path
|
||||||
|
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||||
|
/></g
|
||||||
|
>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row justify-center space-x-2 px-10 pt-6">
|
<div class="flex flex-row justify-center space-x-2 px-10 pt-6">
|
||||||
{#if logs.length === 0}
|
{#if logs.length === 0}
|
||||||
<div class="text-xl font-bold tracking-tighter">{$t('application.build.waiting_logs')}</div>
|
<div class="text-xl font-bold tracking-tighter">{$t('application.build.waiting_logs')}</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="relative">
|
<div class="relative w-full">
|
||||||
<LoadingLogs />
|
<LoadingLogs />
|
||||||
<div class="flex justify-end sticky top-0 p-2">
|
<div class="flex justify-end sticky top-0 p-2">
|
||||||
<button
|
<button
|
||||||
@ -106,7 +173,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="font-mono leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200"
|
class="font-mono w-full leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200"
|
||||||
bind:this={logsEl}
|
bind:this={logsEl}
|
||||||
>
|
>
|
||||||
<div class="px-2">
|
<div class="px-2">
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: res.status,
|
status: res.status,
|
||||||
error: new Error(`Could not load ${endpoint}`)
|
error: new Error(`Could not load ${endpoint}`)
|
||||||
@ -51,14 +50,88 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div class="flex items-center space-x-2 p-5 px-6 font-bold">
|
||||||
<div class="mr-4 text-2xl tracking-tight">
|
<div class="-mb-5 flex-col">
|
||||||
Previews for <a href={application.fqdn} target="_blank">{getDomain(application.fqdn)}</a>
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
||||||
|
Preview Deployments
|
||||||
|
</div>
|
||||||
|
<span class="text-xs">{application.name} </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if applicationSecrets.length !== 0}
|
{#if application.fqdn}
|
||||||
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
<a
|
||||||
|
href={application.fqdn}
|
||||||
|
target="_blank"
|
||||||
|
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
|
<polyline points="15 4 20 4 20 9" />
|
||||||
|
</svg></a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
<a
|
||||||
|
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||||
|
target="_blank"
|
||||||
|
class="w-10"
|
||||||
|
>
|
||||||
|
{#if application.gitSource?.type === 'gitlab'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||||
|
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||||
|
/><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||||
|
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else if application.gitSource?.type === 'github'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<g fill="#ffffff"
|
||||||
|
><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||||
|
/><path
|
||||||
|
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||||
|
/></g
|
||||||
|
>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="mx-auto max-w-6xl px-6 pt-4">
|
||||||
|
<div class="flex justify-center py-4 text-center">
|
||||||
|
<Explainer
|
||||||
|
customClass="w-full"
|
||||||
|
text={applicationSecrets.length === 0
|
||||||
|
? "You can add secrets to PR/MR deployments. Please add secrets to the application first. <br>Useful for creating <span class='text-green-500 font-bold'>staging</span> environments."
|
||||||
|
: "These values overwrite application secrets in PR/MR deployments. <br>Useful for creating <span class='text-green-500 font-bold'>staging</span> environments."}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if applicationSecrets.length !== 0}
|
||||||
<table class="mx-auto border-separate text-left">
|
<table class="mx-auto border-separate text-left">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="h-12">
|
<tr class="h-12">
|
||||||
@ -87,16 +160,9 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
|
||||||
<div class="flex justify-center py-4 text-center">
|
|
||||||
<Explainer
|
|
||||||
customClass="w-full"
|
|
||||||
text={applicationSecrets.length === 0
|
|
||||||
? $t('application.preview.setup_secret_app_first')
|
|
||||||
: $t('application.preview.values_overwriting_app_secrets')}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mx-auto max-w-4xl py-10">
|
<div class="mx-auto max-w-4xl py-10">
|
||||||
<div class="flex flex-wrap justify-center space-x-2">
|
<div class="flex flex-wrap justify-center space-x-2">
|
||||||
{#if containers.length > 0}
|
{#if containers.length > 0}
|
||||||
|
48
src/routes/applications/[id]/secrets/_BatchSecrets.svelte
Normal file
48
src/routes/applications/[id]/secrets/_BatchSecrets.svelte
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<script>
|
||||||
|
export let secrets;
|
||||||
|
export let refreshSecrets;
|
||||||
|
export let id;
|
||||||
|
|
||||||
|
import { saveSecret } from './utils';
|
||||||
|
import pLimit from 'p-limit';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
let batchSecrets = '';
|
||||||
|
function setBatchValue(event) {
|
||||||
|
batchSecrets = event.target?.value;
|
||||||
|
}
|
||||||
|
const limit = pLimit(1);
|
||||||
|
async function getValues(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const eachValuePair = batchSecrets.split('\n');
|
||||||
|
const batchSecretsPairs = eachValuePair
|
||||||
|
.filter((secret) => !secret.startsWith('#') && secret)
|
||||||
|
.map((secret) => {
|
||||||
|
const [name, value] = secret.split('=');
|
||||||
|
const cleanValue = value?.replaceAll('"', '') || '';
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
value: cleanValue,
|
||||||
|
isNew: !secrets.find((secret) => name === secret.name)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
batchSecretsPairs.map(({ name, value, isNew }) =>
|
||||||
|
limit(() => saveSecret({ name, value, applicationId: id, isNew, dispatch }))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
batchSecrets = '';
|
||||||
|
refreshSecrets();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h2 class="title my-6 font-bold">Paste .env file</h2>
|
||||||
|
<form on:submit={getValues} class="mb-12 w-full">
|
||||||
|
<textarea value={batchSecrets} on:change={setBatchValue} class="mb-2 min-h-[200px] w-full" />
|
||||||
|
<button
|
||||||
|
class="bg-green-600 hover:bg-green-500 disabled:text-white disabled:opacity-40"
|
||||||
|
type="submit">Batch add secrets</button
|
||||||
|
>
|
||||||
|
</form>
|
@ -9,12 +9,11 @@
|
|||||||
if (isPRMRSecret) value = PRMRSecret.value;
|
if (isPRMRSecret) value = PRMRSecret.value;
|
||||||
|
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { del, post } from '$lib/api';
|
import { del } from '$lib/api';
|
||||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
import { t } from '$lib/translations';
|
|
||||||
import { toast } from '@zerodevx/svelte-toast';
|
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import { saveSecret } from './utils';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
@ -31,28 +30,20 @@
|
|||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function saveSecret(isNew = false) {
|
|
||||||
if (!name) return errorNotification(`${$t('forms.name')} ${$t('forms.is_required')}`);
|
async function createSecret(isNew) {
|
||||||
if (!value) return errorNotification(`${$t('forms.value')} ${$t('forms.is_required')}`);
|
saveSecret({
|
||||||
try {
|
isNew,
|
||||||
await post(`/applications/${id}/secrets.json`, {
|
name,
|
||||||
name,
|
value,
|
||||||
value,
|
isBuildSecret,
|
||||||
isBuildSecret,
|
isPRMRSecret,
|
||||||
isPRMRSecret,
|
isNewSecret,
|
||||||
isNew
|
applicationId: id,
|
||||||
});
|
dispatch
|
||||||
dispatch('refresh');
|
});
|
||||||
if (isNewSecret) {
|
|
||||||
name = '';
|
|
||||||
value = '';
|
|
||||||
isBuildSecret = false;
|
|
||||||
}
|
|
||||||
toast.push($t('application.secrets.secret_saved'));
|
|
||||||
} catch ({ error }) {
|
|
||||||
return errorNotification(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSecretValue() {
|
function setSecretValue() {
|
||||||
if (isNewSecret) {
|
if (isNewSecret) {
|
||||||
isBuildSecret = !isBuildSecret;
|
isBuildSecret = !isBuildSecret;
|
||||||
@ -134,14 +125,14 @@
|
|||||||
<td>
|
<td>
|
||||||
{#if isNewSecret}
|
{#if isNewSecret}
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<button class="bg-green-600 hover:bg-green-500" on:click={() => saveSecret(true)}
|
<button class="bg-green-600 hover:bg-green-500" on:click={() => createSecret(true)}
|
||||||
>{$t('forms.add')}</button
|
>{$t('forms.add')}</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex flex-row justify-center space-x-2">
|
<div class="flex flex-row justify-center space-x-2">
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<button class="" on:click={() => saveSecret(false)}>{$t('forms.set')}</button>
|
<button class="" on:click={() => createSecret(false)}>{$t('forms.set')}</button>
|
||||||
</div>
|
</div>
|
||||||
{#if !isPRMRSecret}
|
{#if !isPRMRSecret}
|
||||||
<div class="flex justify-center items-end">
|
<div class="flex justify-center items-end">
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { getTeam, getUserDetails } from '$lib/common';
|
import { getUserDetails } from '$lib/common';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { ErrorHandler } from '$lib/database';
|
import { ErrorHandler } from '$lib/database';
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { status, body } = await getUserDetails(event);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
@ -24,7 +24,7 @@ export const get: RequestHandler = async (event) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const post: RequestHandler = async (event) => {
|
export const post: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { status, body } = await getUserDetails(event);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
@ -53,7 +53,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const del: RequestHandler = async (event) => {
|
export const del: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { status, body } = await getUserDetails(event);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
|
@ -25,8 +25,9 @@
|
|||||||
import Secret from './_Secret.svelte';
|
import Secret from './_Secret.svelte';
|
||||||
import { getDomain } from '$lib/components/common';
|
import { getDomain } from '$lib/components/common';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { get } from '$lib/api';
|
import { get } from '$lib/api';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
|
import BatchSecrets from './_BatchSecrets.svelte';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|
||||||
@ -36,13 +37,77 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div class="flex items-center space-x-2 p-5 px-6 font-bold">
|
||||||
<div class="mr-4 text-2xl tracking-tight">
|
<div class="-mb-5 flex-col">
|
||||||
{$t('application.secrets.secrets_for')}
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">{$t('application.secret')}</div>
|
||||||
<a href={application.fqdn} target="_blank">{getDomain(application.fqdn)}</a>
|
<span class="text-xs">{application.name} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if application.fqdn}
|
||||||
|
<a
|
||||||
|
href={application.fqdn}
|
||||||
|
target="_blank"
|
||||||
|
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
|
<polyline points="15 4 20 4 20 9" />
|
||||||
|
</svg></a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
<a
|
||||||
|
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||||
|
target="_blank"
|
||||||
|
class="w-10"
|
||||||
|
>
|
||||||
|
{#if application.gitSource?.type === 'gitlab'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||||
|
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||||
|
/><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||||
|
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else if application.gitSource?.type === 'github'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<g fill="#ffffff"
|
||||||
|
><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||||
|
/><path
|
||||||
|
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||||
|
/></g
|
||||||
|
>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
<div class="mx-auto max-w-6xl px-6 pt-4">
|
||||||
<table class="mx-auto border-separate text-left">
|
<table class="mx-auto border-separate text-left">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="h-12">
|
<tr class="h-12">
|
||||||
@ -72,4 +137,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<BatchSecrets {secrets} {id} {refreshSecrets} />
|
||||||
</div>
|
</div>
|
||||||
|
46
src/routes/applications/[id]/secrets/utils.ts
Normal file
46
src/routes/applications/[id]/secrets/utils.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
|
import { errorNotification } from '$lib/form';
|
||||||
|
import { post } from '$lib/api';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isNew: boolean;
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
isBuildSecret?: boolean;
|
||||||
|
isPRMRSecret?: boolean;
|
||||||
|
isNewSecret?: boolean;
|
||||||
|
applicationId: string;
|
||||||
|
dispatch: (name: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function saveSecret({
|
||||||
|
isNew,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
isBuildSecret,
|
||||||
|
isPRMRSecret,
|
||||||
|
isNewSecret,
|
||||||
|
applicationId,
|
||||||
|
dispatch
|
||||||
|
}: Props): Promise<void> {
|
||||||
|
if (!name) return errorNotification('Name is required.');
|
||||||
|
if (!value) return errorNotification('Value is required.');
|
||||||
|
try {
|
||||||
|
await post(`/applications/${applicationId}/secrets.json`, {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
isBuildSecret,
|
||||||
|
isPRMRSecret,
|
||||||
|
isNew: isNew || false
|
||||||
|
});
|
||||||
|
dispatch('refresh');
|
||||||
|
if (isNewSecret) {
|
||||||
|
name = '';
|
||||||
|
value = '';
|
||||||
|
isBuildSecret = false;
|
||||||
|
}
|
||||||
|
toast.push('Secret saved.');
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
@ -37,12 +37,77 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div class="flex items-center space-x-2 p-5 px-6 font-bold">
|
||||||
<div class="mr-4 text-2xl tracking-tight">
|
<div class="-mb-5 flex-col">
|
||||||
Persistent storage for <a href={application.fqdn} target="_blank"
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
||||||
>{getDomain(application.fqdn)}</a
|
Persistent Storage
|
||||||
>
|
</div>
|
||||||
|
<span class="text-xs">{application.name} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if application.fqdn}
|
||||||
|
<a
|
||||||
|
href={application.fqdn}
|
||||||
|
target="_blank"
|
||||||
|
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
|
<polyline points="15 4 20 4 20 9" />
|
||||||
|
</svg></a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
<a
|
||||||
|
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||||
|
target="_blank"
|
||||||
|
class="w-10"
|
||||||
|
>
|
||||||
|
{#if application.gitSource?.type === 'gitlab'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||||
|
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||||
|
/><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||||
|
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else if application.gitSource?.type === 'github'}
|
||||||
|
<svg viewBox="0 0 128 128" class="icons">
|
||||||
|
<g fill="#ffffff"
|
||||||
|
><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||||
|
/><path
|
||||||
|
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||||
|
/></g
|
||||||
|
>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
||||||
|
@ -31,37 +31,20 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Clickhouse from '$lib/components/svg/databases/Clickhouse.svelte';
|
import DatabaseLinks from '$lib/components/DatabaseLinks.svelte';
|
||||||
import CouchDb from '$lib/components/svg/databases/CouchDB.svelte';
|
|
||||||
import MongoDb from '$lib/components/svg/databases/MongoDB.svelte';
|
|
||||||
import MySql from '$lib/components/svg/databases/MySQL.svelte';
|
|
||||||
import PostgreSql from '$lib/components/svg/databases/PostgreSQL.svelte';
|
|
||||||
import Redis from '$lib/components/svg/databases/Redis.svelte';
|
|
||||||
|
|
||||||
export let database;
|
export let database;
|
||||||
export let settings;
|
export let settings;
|
||||||
export let privatePort;
|
export let privatePort;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex items-center space-x-2 p-6 text-2xl font-bold">
|
<div class="flex items-center space-x-2 p-6 text-2xl font-bold">
|
||||||
<div class="md:max-w-64 truncate text-base tracking-tight md:block md:text-2xl">
|
<div class="-mb-5 flex-col">
|
||||||
{database.name}
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
||||||
|
Configuration
|
||||||
|
</div>
|
||||||
|
<span class="text-xs">{database.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="relative">
|
<DatabaseLinks {database} />
|
||||||
{#if database.type === 'clickhouse'}
|
|
||||||
<Clickhouse />
|
|
||||||
{:else if database.type === 'couchdb'}
|
|
||||||
<CouchDb />
|
|
||||||
{:else if database.type === 'mongodb'}
|
|
||||||
<MongoDb />
|
|
||||||
{:else if database.type === 'mysql'}
|
|
||||||
<MySql />
|
|
||||||
{:else if database.type === 'postgresql'}
|
|
||||||
<PostgreSql />
|
|
||||||
{:else if database.type === 'redis'}
|
|
||||||
<Redis />
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Databases bind:database {privatePort} {settings} />
|
<Databases bind:database {privatePort} {settings} />
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
emailEl.focus();
|
emailEl.focus();
|
||||||
});
|
});
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
|
// Prevent double submission
|
||||||
|
if (loading) return;
|
||||||
|
|
||||||
if (password !== passwordCheck) {
|
if (password !== passwordCheck) {
|
||||||
return errorNotification($t('forms.passwords_not_match'));
|
return errorNotification($t('forms.passwords_not_match'));
|
||||||
}
|
}
|
||||||
@ -89,8 +92,13 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex space-x-2 h-8 items-center justify-center pt-8">
|
<div class="flex space-x-2 h-8 items-center justify-center pt-8">
|
||||||
<button type="submit" class="hover:bg-coollabs-100 text-white bg-coollabs"
|
<button
|
||||||
>{$t('register.register')}</button
|
type="submit"
|
||||||
|
class="hover:bg-coollabs-100 text-white"
|
||||||
|
disabled={loading}
|
||||||
|
class:bg-transparent={loading}
|
||||||
|
class:text-stone-600={loading}
|
||||||
|
class:bg-coollabs={!loading}>{loading ? $t('register.registering') : $t('register.register')}</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
19
src/routes/services/[id]/_Services/_MeiliSearch.svelte
Normal file
19
src/routes/services/[id]/_Services/_MeiliSearch.svelte
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
|
export let service;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
|
<div class="title">MeiliSearch</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="masterKey">Admin API key</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
id="masterKey"
|
||||||
|
isPasswordField
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
name="masterKey"
|
||||||
|
value={service.meiliSearch.masterKey}
|
||||||
|
/>
|
||||||
|
</div>
|
@ -12,6 +12,7 @@
|
|||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
import { toast } from '@zerodevx/svelte-toast';
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
import Ghost from './_Ghost.svelte';
|
import Ghost from './_Ghost.svelte';
|
||||||
|
import MeiliSearch from './_MeiliSearch.svelte';
|
||||||
import MinIo from './_MinIO.svelte';
|
import MinIo from './_MinIO.svelte';
|
||||||
import PlausibleAnalytics from './_PlausibleAnalytics.svelte';
|
import PlausibleAnalytics from './_PlausibleAnalytics.svelte';
|
||||||
import VsCodeServer from './_VSCodeServer.svelte';
|
import VsCodeServer from './_VSCodeServer.svelte';
|
||||||
@ -113,9 +114,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 px-10">
|
<div class="grid grid-cols-2 px-10">
|
||||||
<div class="flex-col ">
|
<div class="flex-col ">
|
||||||
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
|
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100">{$t('application.url_fqdn')}</label>
|
||||||
>{$t('application.domain_fqdn')}</label
|
|
||||||
>
|
|
||||||
<Explainer text={$t('application.https_explainer')} />
|
<Explainer text={$t('application.https_explainer')} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -150,6 +149,8 @@
|
|||||||
<Wordpress bind:service {isRunning} {readOnly} />
|
<Wordpress bind:service {isRunning} {readOnly} />
|
||||||
{:else if service.type === 'ghost'}
|
{:else if service.type === 'ghost'}
|
||||||
<Ghost bind:service {readOnly} />
|
<Ghost bind:service {readOnly} />
|
||||||
|
{:else if service.type === 'meilisearch'}
|
||||||
|
<MeiliSearch bind:service />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
|
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
|
||||||
import Ghost from '$lib/components/svg/services/Ghost.svelte';
|
import Ghost from '$lib/components/svg/services/Ghost.svelte';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
|
import MeiliSearch from '$lib/components/svg/services/MeiliSearch.svelte';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
const from = $page.url.searchParams.get('from');
|
const from = $page.url.searchParams.get('from');
|
||||||
@ -87,6 +88,8 @@
|
|||||||
<UptimeKuma isAbsolute />
|
<UptimeKuma isAbsolute />
|
||||||
{:else if type.name === 'ghost'}
|
{:else if type.name === 'ghost'}
|
||||||
<Ghost isAbsolute />
|
<Ghost isAbsolute />
|
||||||
|
{:else if type.name === 'meilisearch'}
|
||||||
|
<MeiliSearch isAbsolute />
|
||||||
{/if}{type.fancyName}
|
{/if}{type.fancyName}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -29,19 +29,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import PlausibleAnalytics from '$lib/components/svg/services/PlausibleAnalytics.svelte';
|
|
||||||
import NocoDb from '$lib/components/svg/services/NocoDB.svelte';
|
|
||||||
import MinIo from '$lib/components/svg/services/MinIO.svelte';
|
|
||||||
import VsCodeServer from '$lib/components/svg/services/VSCodeServer.svelte';
|
|
||||||
import Wordpress from '$lib/components/svg/services/Wordpress.svelte';
|
|
||||||
import Services from './_Services/_Services.svelte';
|
|
||||||
import VaultWarden from '$lib/components/svg/services/VaultWarden.svelte';
|
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
import LanguageTool from '$lib/components/svg/services/LanguageTool.svelte';
|
import ServiceLinks from '$lib/components/ServiceLinks.svelte';
|
||||||
import N8n from '$lib/components/svg/services/N8n.svelte';
|
import Services from './_Services/_Services.svelte';
|
||||||
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
|
|
||||||
import Ghost from '$lib/components/svg/services/Ghost.svelte';
|
|
||||||
|
|
||||||
export let service;
|
export let service;
|
||||||
export let isRunning;
|
export let isRunning;
|
||||||
@ -52,13 +43,12 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div class="flex h-20 items-center space-x-2 p-5 px-6 font-bold">
|
||||||
class="flex items-center space-x-3 px-6 text-2xl font-bold"
|
<div class="-mb-5 flex-col">
|
||||||
class:p-5={service.fqdn}
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
||||||
class:p-6={!service.fqdn}
|
Configuration
|
||||||
>
|
</div>
|
||||||
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
<span class="text-xs">{service.name}</span>
|
||||||
{service.name}
|
|
||||||
</div>
|
</div>
|
||||||
{#if service.fqdn}
|
{#if service.fqdn}
|
||||||
<a
|
<a
|
||||||
@ -83,49 +73,7 @@
|
|||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div>
|
<ServiceLinks {service} />
|
||||||
{#if service.type === 'plausibleanalytics'}
|
|
||||||
<a href="https://plausible.io" target="_blank">
|
|
||||||
<PlausibleAnalytics />
|
|
||||||
</a>
|
|
||||||
{:else if service.type === 'nocodb'}
|
|
||||||
<a href="https://nocodb.com" target="_blank">
|
|
||||||
<NocoDb />
|
|
||||||
</a>
|
|
||||||
{:else if service.type === 'minio'}
|
|
||||||
<a href="https://min.io" target="_blank">
|
|
||||||
<MinIo />
|
|
||||||
</a>
|
|
||||||
{:else if service.type === 'vscodeserver'}
|
|
||||||
<a href="https://coder.com" target="_blank">
|
|
||||||
<VsCodeServer />
|
|
||||||
</a>
|
|
||||||
{:else if service.type === 'wordpress'}
|
|
||||||
<a href="https://wordpress.org" target="_blank">
|
|
||||||
<Wordpress />
|
|
||||||
</a>
|
|
||||||
{:else if service.type === 'vaultwarden'}
|
|
||||||
<a href="https://github.com/dani-garcia/vaultwarden" target="_blank">
|
|
||||||
<VaultWarden />
|
|
||||||
</a>
|
|
||||||
{:else if service.type === 'languagetool'}
|
|
||||||
<a href="https://languagetool.org/dev" target="_blank">
|
|
||||||
<LanguageTool />
|
|
||||||
</a>
|
|
||||||
{:else if service.type === 'n8n'}
|
|
||||||
<a href="https://n8n.io" target="_blank">
|
|
||||||
<N8n />
|
|
||||||
</a>
|
|
||||||
{:else if service.type === 'uptimekuma'}
|
|
||||||
<a href="https://github.com/louislam/uptime-kuma" target="_blank">
|
|
||||||
<UptimeKuma />
|
|
||||||
</a>
|
|
||||||
{:else if service.type === 'ghost'}
|
|
||||||
<a href="https://ghost.org" target="_blank">
|
|
||||||
<Ghost />
|
|
||||||
</a>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Services bind:service {isRunning} {readOnly} />
|
<Services bind:service {isRunning} {readOnly} />
|
||||||
|
@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.updateLanguageToolService({ id, fqdn, name });
|
await db.updateMeiliSearchService({ id, fqdn, name });
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
21
src/routes/services/[id]/meilisearch/index.json.ts
Normal file
21
src/routes/services/[id]/meilisearch/index.json.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { getUserDetails } from '$lib/common';
|
||||||
|
import * as db from '$lib/database';
|
||||||
|
import { ErrorHandler } from '$lib/database';
|
||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const post: RequestHandler = async (event) => {
|
||||||
|
const { status, body } = await getUserDetails(event);
|
||||||
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
|
const { id } = event.params;
|
||||||
|
|
||||||
|
let { name, fqdn } = await event.request.json();
|
||||||
|
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.updateLanguageToolService({ id, fqdn, name });
|
||||||
|
return { status: 201 };
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorHandler(error);
|
||||||
|
}
|
||||||
|
};
|
83
src/routes/services/[id]/meilisearch/start.json.ts
Normal file
83
src/routes/services/[id]/meilisearch/start.json.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { asyncExecShell, createDirectories, getEngine, getUserDetails } from '$lib/common';
|
||||||
|
import * as db from '$lib/database';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
import { ErrorHandler, getServiceImage } from '$lib/database';
|
||||||
|
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||||
|
|
||||||
|
export const post: RequestHandler = async (event) => {
|
||||||
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
|
const { id } = event.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const service = await db.getService({ id, teamId });
|
||||||
|
const {
|
||||||
|
meiliSearch: { masterKey }
|
||||||
|
} = service;
|
||||||
|
const { type, version, destinationDockerId, destinationDocker, serviceSecret } = service;
|
||||||
|
const network = destinationDockerId && destinationDocker.network;
|
||||||
|
const host = getEngine(destinationDocker.engine);
|
||||||
|
|
||||||
|
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||||
|
const image = getServiceImage(type);
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
volume: `${id}-datams:/data.ms`,
|
||||||
|
environmentVariables: {
|
||||||
|
MEILI_MASTER_KEY: masterKey
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (serviceSecret.length > 0) {
|
||||||
|
serviceSecret.forEach((secret) => {
|
||||||
|
config.environmentVariables[secret.name] = secret.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const composeFile = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[id]: {
|
||||||
|
container_name: id,
|
||||||
|
image: config.image,
|
||||||
|
networks: [network],
|
||||||
|
environment: config.environmentVariables,
|
||||||
|
restart: 'always',
|
||||||
|
volumes: [config.volume],
|
||||||
|
labels: makeLabelForServices('meilisearch')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
networks: {
|
||||||
|
[network]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: {
|
||||||
|
[config.volume.split(':')[0]]: {
|
||||||
|
name: config.volume.split(':')[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||||
|
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (version === 'latest') {
|
||||||
|
await asyncExecShell(
|
||||||
|
`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||||
|
return {
|
||||||
|
status: 200
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorHandler(error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorHandler(error);
|
||||||
|
}
|
||||||
|
};
|
35
src/routes/services/[id]/meilisearch/stop.json.ts
Normal file
35
src/routes/services/[id]/meilisearch/stop.json.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { getUserDetails, removeDestinationDocker } from '$lib/common';
|
||||||
|
import * as db from '$lib/database';
|
||||||
|
import { ErrorHandler } from '$lib/database';
|
||||||
|
import { checkContainer } from '$lib/haproxy';
|
||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const post: RequestHandler = async (event) => {
|
||||||
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
|
const { id } = event.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const service = await db.getService({ id, teamId });
|
||||||
|
const { destinationDockerId, destinationDocker, fqdn } = service;
|
||||||
|
if (destinationDockerId) {
|
||||||
|
const engine = destinationDocker.engine;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const found = await checkContainer(engine, id);
|
||||||
|
if (found) {
|
||||||
|
await removeDestinationDocker({ id, engine });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 200
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorHandler(error);
|
||||||
|
}
|
||||||
|
};
|
@ -27,6 +27,7 @@
|
|||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { get } from '$lib/api';
|
import { get } from '$lib/api';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
|
import ServiceLinks from '$lib/components/ServiceLinks.svelte';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|
||||||
@ -36,13 +37,39 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div
|
||||||
<div class="mr-4 text-2xl tracking-tight">
|
class="flex items-center space-x-2 p-5 px-6 font-bold"
|
||||||
{$t('application.secret')}
|
class:p-5={service.fqdn}
|
||||||
{#if service.fqdn}
|
class:p-6={!service.fqdn}
|
||||||
<a href={service.fqdn} target="_blank">{getDomain(service.fqdn)}</a>
|
>
|
||||||
{/if}
|
<div class="-mb-5 flex-col">
|
||||||
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">{$t('application.secret')}</div>
|
||||||
|
<span class="text-xs">{service.name}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{#if service.fqdn}
|
||||||
|
<a
|
||||||
|
href={service.fqdn}
|
||||||
|
target="_blank"
|
||||||
|
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
|
<polyline points="15 4 20 4 20 9" />
|
||||||
|
</svg></a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<ServiceLinks {service} />
|
||||||
</div>
|
</div>
|
||||||
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
||||||
<table class="mx-auto border-separate text-left">
|
<table class="mx-auto border-separate text-left">
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
|
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
|
||||||
import Ghost from '$lib/components/svg/services/Ghost.svelte';
|
import Ghost from '$lib/components/svg/services/Ghost.svelte';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
|
import MeiliSearch from '$lib/components/svg/services/MeiliSearch.svelte';
|
||||||
|
|
||||||
export let services;
|
export let services;
|
||||||
async function newService() {
|
async function newService() {
|
||||||
@ -68,6 +69,8 @@
|
|||||||
<UptimeKuma isAbsolute />
|
<UptimeKuma isAbsolute />
|
||||||
{:else if service.type === 'ghost'}
|
{:else if service.type === 'ghost'}
|
||||||
<Ghost isAbsolute />
|
<Ghost isAbsolute />
|
||||||
|
{:else if service.type === 'meilisearch'}
|
||||||
|
<MeiliSearch isAbsolute />
|
||||||
{/if}
|
{/if}
|
||||||
<div class="font-bold text-xl text-center truncate">
|
<div class="font-bold text-xl text-center truncate">
|
||||||
{service.name}
|
{service.name}
|
||||||
|
@ -123,7 +123,7 @@
|
|||||||
<div class="grid grid-cols-2 items-start">
|
<div class="grid grid-cols-2 items-start">
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div class="pt-2 text-base font-bold text-stone-100">
|
<div class="pt-2 text-base font-bold text-stone-100">
|
||||||
{$t('application.domain_fqdn')}
|
{$t('application.url_fqdn')}
|
||||||
</div>
|
</div>
|
||||||
<Explainer text={$t('setting.ssl_explainer')} />
|
<Explainer text={$t('setting.ssl_explainer')} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,10 +54,10 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#svelte .item.hover {
|
#svelte .item.hover {
|
||||||
@apply bg-coolgray-100 text-white;
|
@apply bg-coollabs text-white !important;
|
||||||
}
|
}
|
||||||
#svelte .item.active {
|
#svelte .item.active {
|
||||||
@apply bg-coollabs text-white;
|
@apply bg-coolgray-100 text-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user